public override Func <IQueryable <T>, IQueryable <T> > Apply <T>() { if (keySet == null) { Type summType = ContentTypeHierarchy.SummaryTypes[this.refType]; ReflectionX.InvokeGenericMethod(this, "SetKeySet", false, mi => true, new List <Type> { this.refType, summType }); } // encodes x => x.Prop var xParam = Expression.Parameter(typeof(T), "x"); var accessProp = Expression.MakeMemberAccess(xParam, this.PropInfo); var selector = Expression.Lambda(accessProp, xParam); Type propType = this.PropInfo.PropertyType; Dictionary <Type, TypeConverter> converters = this.keySet.Select(k => k.GetType()).Distinct().ToDictionary(t => t, t => TypeDescriptor.GetConverter(t)); this.keySet = this.keySet.Select(k => k.GetType() == propType ? k : converters[k.GetType()].ConvertTo(k, propType)).ToList(); // if foreign key field was nullable return(iq => iq.WhereIn(selector, this.keySet, propType)); }
protected void DoEditAction(object data, string editAction) { IList list; Type itemType; switch (editAction.UpTo("-")) { case "add": list = ReflectionX.GetPropertyValueByPath(data, editAction.After("-")) as IList; itemType = list.GetType().GetGenericArguments()[0]; if (list != null) { list.Add(CreateInstance(itemType)); } break; case "del": list = ReflectionX.GetPropertyValueByPath(data, editAction.After("-").UpToLast("[")) as IList; itemType = list.GetType().GetGenericArguments()[0]; if (list != null) { ModelState.Clear(); // templating system will take old values out of the ModelState unless you do this list.RemoveAt(int.Parse(editAction.LastAfter("[").UpTo("]"))); } break; } }
/// <summary> /// Get containers by query /// </summary> /// <typeparam name="T">The type of the resulting containers</typeparam> /// <param name="targetType">The content type or summary type of the intended output</param> /// <param name="types">The allowed types of contained content items returned</param> /// <param name="queryBody">An operator on an IQueryable of the container type to filter the ones to return</param> /// <returns>Resulting list of containers</returns> public virtual IEnumerable <T> Get <T>(Type targetType, IEnumerable <Type> types, Func <IQueryable <T>, IQueryable <T> > queryBody) where T : class { if (types == null || !types.Any()) { yield break; } if (types.Count() == 1) { var contentType = types.Single(); var itemEnum = (IEnumerable)ReflectionX.InvokeGenericMethod(this, "BasicGet", new Type[] { contentType, typeof(T) }, targetType, queryBody); foreach (object res in itemEnum) { yield return(res as T); } } else { foreach (Type t in types) { foreach (var res in this.Get <T>(targetType, new Type[] { t }, queryBody)) { yield return(res); } } } }
public virtual ActionResult PropertyItemHtml(object data, string propertyPath, int depth, string pathPrefix) { ViewData["propertyPath"] = (pathPrefix ?? "") + propertyPath; ViewData["addDepth"] = depth - 1; string parentPath = propertyPath.Contains(".") ? propertyPath.UpToLast(".") : ""; string propertyName = (propertyPath.Contains(".") ? propertyPath.LastAfter(".") : propertyPath).UpTo("["); Type parentType = ReflectionX.GetPropertyTypeByPath(data.GetType(), parentPath); IList list = ReflectionX.GetPropertyValueByPath(data, propertyPath, true) as IList; var listProp = ReflectionX.GetPropertyByPath(data.GetType(), propertyPath); Type listType = listProp.PropertyType; if (listType.GetType().IsArray) { list = (IList)Array.CreateInstance(ReflectionX.ElementType(listType), 1); list[0] = CreateInstance(listType.GetElementType()); } else { list = (IList)Activator.CreateInstance(listType); list.Add(CreateInstance(ReflectionX.ElementType(listType))); } ViewData["list"] = list; var metadata = new DataAnnotationsModelMetadataProvider().GetMetadataForProperty(null, parentType, propertyName); ViewData["CollectionAdditionalValues"] = metadata.AdditionalValues; RouteData.DataTokens.Add("CancelProcessingHtml", true); return(PartialView(ConfigHelper.GetViewPath("LyniconPropertyItem.cshtml"), data)); }
/// <summary> /// Get an IQueryable in the extended type given the base type, sourcing data without fetching /// non-summarised fields /// </summary> /// <param name="tBase">The base type</param> /// <returns>IQueryable in extended type which doesn't fetch non-summarised fields</returns> public IQueryable SummarisedSet(Type tBase) { var extender = LyniconSystem.Instance.Extender; if (!extender.BaseTypes.Contains(tBase)) { throw new Exception("No composite of base type " + tBase.FullName); } // Below hack required because constructing the 'project' query (which is .Select(..) under the covers) // causes a database connection to be made, so this must be avoided. The result of the IQueryable returned // is not relevant as it will be ignored, however the item type IS used in TotalCache. if (Repository.Instance.AvoidConnection) { Type itemType = extender[tBase] ?? tBase; Type listType = typeof(List <>).MakeGenericType(itemType); return(((IEnumerable)(Activator.CreateInstance(listType))).AsQueryable()); } Type sumsType = extender.Summarised(tBase); IQueryable q = (IQueryable)ReflectionX.InvokeGenericMethod(this, "SummarisedSet", sumsType); var project = projectors[tBase]; var selectMi = selectors[tBase]; var qryOut = (IQueryable)selectMi.Invoke(null, new object[] { q, project }); return(qryOut); }
/// <summary> /// Get containers by addresses /// </summary> /// <param name="targetType">type of contained items these will produce - either 'object' or a summary type</param> /// <param name="addresses">list of addresses to fetch</param> /// <returns>container(s) at addresses</returns> public IEnumerable <object> Get(Type targetType, IEnumerable <Address> addresses) { foreach (var addressG in addresses.GroupBy(ad => ad.Type)) { Type contT = System.Collator.ContainerType(addressG.Key); var pathPiInfo = contT.GetProperties() .Select(pi => new { pi, a = pi.GetCustomAttribute <AddressComponentAttribute>() }) .FirstOrDefault(pii => pii.a != null && pii.a.UsePath); if (pathPiInfo != null) // container has a single property in which the path is stored - we can get all using same repository in one query { foreach (object res in (IEnumerable)ReflectionX.InvokeGenericMethod(this, "Get", mi => { var parms = mi.GetParameters(); return(parms.Length == 2 && parms[1].Name == "addresses"); }, new Type[] { contT }, targetType, addressG)) { yield return(res); } } else // we have to use a query for each item as the path is spread across multiple fields { foreach (Address a in addressG) { var results = (IEnumerable)ReflectionX.InvokeGenericMethod(this, "Get", m => m.GetParameters()[1].ParameterType == typeof(Address), new Type[] { contT }, a.Type, a); foreach (var res in results) { yield return(res); } } } } }
/// <summary> /// Delete a container from the data source /// </summary> /// <param name="o">The container to delete</param> /// <param name="bypassChecks">Whether to bypass any checks made to stop deletion by a front end user</param> public void Delete(object o, bool bypassChecks) { using (var dataSource = DataSourceFactory.Create(false)) { var idProp = LinqX.GetIdProp(o.GetType(), this.IdName); object noId = ReflectionX.GetDefault(idProp.PropertyType); var eventData = new RepositoryEventData(o, bypassChecks); var eventResult = EventHub.Instance.ProcessEvent("Repository.Set.Delete", this, eventData); var itemDel = ((RepositoryEventData)eventResult.Data).Container; bool wasHandled = ((RepositoryEventData)eventResult.Data).WasHandled; if (itemDel != null) { if (eventResult.EventName.EndsWith("Add")) { DoAdd(dataSource, itemDel, idProp, wasHandled); } else if (eventResult.EventName.EndsWith("Update") && !wasHandled) { dataSource.Update(itemDel); } else if (!wasHandled) { dataSource.Delete(itemDel); } if (!wasHandled) { dataSource.SaveChanges(); } } var savedEventData = new RepositoryEventData(o, bypassChecks); EventHub.Instance.ProcessEvent(eventResult.EventName.Replace("Set", "Saved"), this, savedEventData); } }
private void AddTypeInner(Type t) { if (t.GetInterface("IList") != null) { t = ReflectionX.ElementType(t); } if (TypeProperties.ContainsKey(t.AssemblyQualifiedName)) { return; } var typeDict = new Dictionary <string, string>(); TypeProperties.Add(t.AssemblyQualifiedName, typeDict); foreach (var prop in t.GetPersistedProperties()) { if (typeDict.ContainsKey(prop.Name)) { log.Error("Property " + prop.Name + " repeated on type " + t.FullName); continue; } typeDict.Add(prop.Name, prop.PropertyType.AssemblyQualifiedName); if (!prop.PropertyType.FullName.StartsWith("System.")) { AddTypeInner(prop.PropertyType); } } }
/// <inheritdoc/> public override IEnumerable <T> Get <T>(IEnumerable <Address> addresses) { foreach (var ag in addresses.GroupBy(a => a.Type)) { var typeResults = (IEnumerable <T>)ReflectionX.InvokeGenericMethod(this, "GetAddressesOfType", false, mi => true, new Type[] { typeof(T), ag.Key }, ag); foreach (var res in typeResults) { yield return(res); } } }
public override void List(System.Reflection.PropertyInfo pi, System.Collections.IList val) { if (val == null) { NullProperty = pi; } else { NullProperty = new NoNullObjectCheck().Run(ReflectionX.ElementType(val)) ?? NullProperty; } }
/// <inheritdoc/> public override IEnumerable <T> Get <T>(IEnumerable <ItemId> ids) { foreach (var idg in ids.GroupBy(id => id.Type)) { var typeResults = (IEnumerable <T>)ReflectionX.InvokeGenericMethod(this, "GetIdsOfType", false, mi => true, new Type[] { typeof(T), idg.Key }, idg); foreach (var res in typeResults) { yield return(res); } } }
protected override Expression VisitLambda <T>(Expression <T> node) { Type changedType = ReflectionX.SubstituteType(typeof(T), from, to); if (changedType != null) { return(Expression.Lambda(changedType, Visit(node.Body), node.Parameters.Select(p => (ParameterExpression)Visit(p)))); } return(base.VisitLambda <T>(node)); }
public IActionResult ScanReferences() { List <string> errors = new List <string>(); foreach (Type t in ContentTypeHierarchy.AllContentTypes) { List <string> refErrors = (List <string>)ReflectionX.InvokeGenericMethod(this, "GetReferenceErrors", t); errors.AddRange(refErrors); } return(PartialView(errors)); }
/// <summary> /// Get markup to show all the items of a type in a paged box on the List page /// </summary> /// <param name="datatype">The data type</param> /// <returns>Markup of the paged box listing the items</returns> public ActionResult GetPage(string datatype) { ViewData.Add("UrlPermission", LyniconSecurityManager.Current.CurrentUserInRole(Lynicon.Membership.User.EditorRole)); ViewData.Add("DelPermission", LyniconSecurityManager.Current.CurrentUserInRole(Lynicon.Membership.User.AdminRole)); Type type = ContentTypeHierarchy.GetContentType(datatype); Type containerType = Collator.Instance.ContainerType(type); // invoke Collator.Instance.GetList<Summary, type>(new Type[] { type }, RouteData).ToArray(); var summs = (IEnumerable <Summary>)ReflectionX.InvokeGenericMethod(Collator.Instance, "GetList", new Type[] { typeof(Summary), containerType }, new Type[] { type }, RouteData); var data = summs.ToArray(); return(PartialView("ItemPage", data)); }
public object GetNew(Type type, RouteData rd) { object newObj = Activator.CreateInstance(type); if (rd != null && rd.Values.ContainsKey("@id")) { string idName = (Repository.Instance.Registered(type) as BasicRepository).GetIdName(type); PropertyInfo prop = type.GetProperty(idName); prop.SetValue(newObj, ReflectionX.ChangeType(rd.Values["@id"], prop.PropertyType)); } return(newObj); }
private object ConvertForField(Type type, string propertyName, object val) { Type propertyType = type.GetProperty(propertyName).PropertyType; if (val.GetType() != propertyType) { return(ReflectionX.ChangeType(val, propertyType)); } else { return(val); } }
/// <summary> /// Get a DbQuery (in the extended type) for a given base type /// </summary> /// <param name="tBase">The base type</param> /// <param name="useIncludes">Whether to use includes specified by AlwaysIncludesAttribute</param> /// <returns>A DbQuery against the underlying database</returns> public IQueryable CompositeSet(Type tBase, bool useIncludes) { var extender = LyniconSystem.Instance.Extender; var ext = extender[tBase]; if (ext == null) { throw new Exception("No composite of base type " + tBase.FullName); } IQueryable q = (IQueryable)ReflectionX.InvokeGenericMethod(this, "InnerCompositeSet", ext, true, useIncludes); return(q); }
/// <summary> /// Get containers by ids /// </summary> /// <typeparam name="T">The type of the resulting containers</typeparam> /// <param name="targetType">The content type or summary type of the intended output</param> /// <param name="ids">The ItemIds of the containers to fetch</param> /// <returns></returns> public virtual IEnumerable <T> Get <T>(Type targetType, IEnumerable <ItemId> ids) where T : class { bool isSummary = typeof(Summary).IsAssignableFrom(targetType); TContext db = GetDb(); try { foreach (var idg in ids.GroupBy(ii => ii.Type)) { if (idg.Count() > MaximumIdBatchSize) { throw new ArgumentException("Request for too many ids at once, request in batches of maximum size " + MaximumIdBatchSize); } var qed = new QueryEventData <IQueryable> { QueryBody = (Func <IQueryable, IQueryable>)ReflectionX.InvokeGenericMethod(this, "GetIdsQuery", idg.Key, idg.Select(ii => ii.Id)) }; qed.Source = ApplyIncludes(idg.Key, ((TContext)db).Set(idg.Key)); qed.Ids = ids; qed = EventHub.Instance.ProcessEvent(isSummary ? "Repository.Get.Summaries.Ids" : "Repository.Get.Items.Ids", this, qed).Data as QueryEventData <IQueryable>; if (qed.EnumSource != null) { foreach (var res in qed.EnumSource) { yield return(res as T); } } else { foreach (var res in qed.QueryBody(qed.Source).AsFacade <T>()) { yield return(res); } } } } finally { if (!isSummary) { EndCallDb((TContext)db); } } }
/// <summary> /// Get a data item, or list of items, via the route which maps to them /// </summary> /// <typeparam name="T">type of the item(s), a generic list if a list of items, could be a summary type</typeparam> /// <param name="contentType">the content type of the item(s)</param> /// <param name="rd">route data</param> /// <returns>the mapped items(s)</returns> public T Get <T>(Type contentType, RouteData rd) where T : class { //CodeTimer.MarkTime("Get via route START"); try { if (typeof(T).IsGenericType() && typeof(T).GetGenericTypeDefinition() == typeof(List <>)) { Type elType = typeof(T).GetGenericArguments()[0]; ICollator coll; List <Type> contentTypes; bool isSummary = typeof(Summary).IsAssignableFrom(elType); if (isSummary) { contentTypes = ContentTypeHierarchy.GetSummaryContainers(elType); if (contentTypes.Select(ct => Registered(ct)).Distinct().Count() != 1) { throw new Exception("Content types containing summary type " + elType.FullName + " dont have 1 unique registered collator, requirement for a dataroute with list type"); } coll = Registered(contentTypes.First()); } else { coll = Registered(elType); contentTypes = new List <Type> { elType }; } T itemList = (T)ReflectionX.InvokeGenericMethod(coll, "GetList", new Type[] { elType, isSummary ? elType : ContainerType(elType) }, contentTypes, rd); return(itemList); } else { ICollator coll = Registered(contentType); return(coll.Get <T>(new List <Address> { coll.GetAddress(contentType, rd) }).FirstOrDefault()); } } finally { //CodeTimer.MarkTime("Get via route END"); } }
protected override Expression VisitParameter(ParameterExpression node) { Type changedType = ReflectionX.SubstituteType(node.Type, from, to); if (changedType != null) { if (!parameterMappings.ContainsKey(node)) { parameterMappings.Add(node, Expression.Parameter(changedType, node.Name)); } return(parameterMappings[node]); } return(base.VisitParameter(node)); }
/// <summary> /// Get total count of items of a given type in api /// </summary> /// <param name="targetType">type type of item to count</param> /// <returns>count</returns> public int GetCount(Type targetType) { Type type = targetType; if (Registered(targetType) is ContentRepository) { type = typeof(ContentItem); } Type iqType = typeof(IQueryable <>).MakeGenericType(type); ParameterExpression iqParam = Expression.Parameter(iqType, "iq"); LambdaExpression queryExp = Expression.Lambda(iqParam, iqParam); return((int)ReflectionX.InvokeGenericMethod(this, "GetCount", type, new Type[] { targetType }, queryExp.Compile())); }
public override Func <IQueryable <T>, IQueryable <T> > Apply <T>() { if (keySet == null) { Type summType = ContentTypeHierarchy.SummaryTypes[this.refType]; ReflectionX.InvokeGenericMethod(this, "SetKeySet", false, mi => true, new List <Type> { this.refType, summType }); } // encodes x => x.Prop var xParam = Expression.Parameter(typeof(T), "x"); var accessProp = Expression.MakeMemberAccess(xParam, this.PropInfo); var selector = Expression.Lambda(accessProp, xParam); Type propType = this.PropInfo.PropertyType; // if foreign key field was nullable return(iq => iq.WhereIn(selector, this.keySet, propType)); }
/// <summary> /// Delete a container from the data source /// </summary> /// <param name="o">The container to delete</param> /// <param name="bypassChecks">Whether to bypass any checks made to stop deletion by a front end user</param> public void Delete(object o, bool bypassChecks) { var db = GetDb(); try { var idProp = LinqX.GetIdProp(o.GetType(), this.IdName); object noId = ReflectionX.GetDefault(idProp.PropertyType); var eventData = new RepositoryEventData(o, bypassChecks); var eventResult = EventHub.Instance.ProcessEvent("Repository.Set.Delete", this, eventData); var itemDel = ((RepositoryEventData)eventResult.Data).Container; bool wasHandled = ((RepositoryEventData)eventResult.Data).WasHandled; if (itemDel != null) { if (eventResult.EventName.EndsWith("Add")) { DoAdd(db, itemDel, idProp, wasHandled); } else if (eventResult.EventName.EndsWith("Update") && !wasHandled) { db.Entry(itemDel).State = EntityState.Modified; } else if (!wasHandled) { db.Entry(itemDel).State = EntityState.Deleted; } if (!wasHandled) { db.SaveChanges(); } } var savedEventData = new RepositoryEventData(o, bypassChecks); EventHub.Instance.ProcessEvent(eventResult.EventName.Replace("Set", "Saved"), this, savedEventData); } finally { EndCallDb(db); } }
/// <summary> /// Get containers by ids /// </summary> /// <typeparam name="T">The type of the resulting containers</typeparam> /// <param name="targetType">The content type or summary type of the intended output</param> /// <param name="ids">The ItemIds of the containers to fetch</param> /// <returns></returns> public IEnumerable <T> Get <T>(Type targetType, IEnumerable <ItemId> ids) where T : class { bool isSummary = typeof(Summary).IsAssignableFrom(targetType); using (var dataSource = DataSourceFactory.Create(isSummary)) { foreach (var idg in ids.GroupBy(ii => ii.Type)) { if (idg.Count() > MaximumIdBatchSize) { throw new ArgumentException("Request for too many ids at once, request in batches of maximum size " + MaximumIdBatchSize); } var qed = new QueryEventData <IQueryable> { QueryBody = (Func <IQueryable, IQueryable>)ReflectionX.InvokeGenericMethod(this, "GetIdsQuery", idg.Key, idg.Select(ii => ii.Id)), Source = dataSource.GetSource(idg.Key), Ids = ids }; qed = EventHub.Instance.ProcessEvent(isSummary ? "Repository.Get.Summaries.Ids" : "Repository.Get.Items.Ids", this, qed).Data as QueryEventData <IQueryable>; if (qed.EnumSource != null) { foreach (var res in qed.EnumSource) { yield return(res as T); } } else { foreach (var res in qed.QueryBody(qed.Source).AsFacade <T>()) { yield return(res); } } } } }
protected override Expression VisitMethodCall(MethodCallExpression node) { var mi = node.Method; if (mi.IsGenericMethod) { Type[] changedTypes = ReflectionX.SubstituteTypes(mi.GetGenericArguments(), from, to); if (changedTypes != null) { MethodInfo newMi = mi.GetGenericMethodDefinition().MakeGenericMethod(changedTypes); if (node.Object == null) { return(Expression.Call(newMi, node.Arguments.Select(a => Visit(a)))); } else { return(Expression.Call(node.Object, newMi, node.Arguments.Select(a => Visit(a)))); } } } return(base.VisitMethodCall(node)); }
/// <summary> /// Get new item whose address is given /// </summary> /// <param name="a">the address to create it at</param> /// <returns>the new item</returns> public object GetNew(Address a) { return(ReflectionX.InvokeGenericMethod(this, "GetNew", mi => mi.GetParameters().First().ParameterType == typeof(Address) && mi.GetGenericArguments().Length == 1, new Type[] { a.Type }, a)); }
/// <summary> /// Get new item whose data address is given by a specified route /// </summary> /// <param name="type">type of new item</param> /// <param name="rd">the specified route</param> /// <returns>the new item</returns> public object GetNew(Type type, RouteData rd) { return(ReflectionX.InvokeGenericMethod(this, "GetNew", mi => mi.GetParameters().First().ParameterType == typeof(RouteData), new Type[] { type }, rd)); }
/// <summary> /// Apply a sort to a result row list based on the settings in this FieldFilter /// </summary> /// <param name="source">The result row list</param> /// <returns>The sorted result row list</returns> public override IEnumerable <Tuple <object, Summary> > ApplySort(IEnumerable <Tuple <object, Summary> > source) { return((IEnumerable <Tuple <object, Summary> >)ReflectionX.InvokeGenericMethod(this, "ApplySort", false, mi => true, new Type[] { PropInfo.PropertyType }, source)); }
public IActionResult Rename(string dataType, string path, bool pathInSummary, string newPath, bool newPathInSummary, bool?isDelete) { Type contentType = ContentTypeHierarchy.GetContentType(dataType); if (contentType == null) { return(Content("No such type, remember to add 'Content' where necessary")); } int propMissing = 0; int updated = 0; // create a new ContentRepository so we can bypass any block to writing caused by existing data problems // on the global data api var repo = new ContentRepository(new CoreDataSourceFactory(LyniconSystem.Instance)); repo.BypassChangeProblems = true; foreach (ContentItem ci in repo.Get <ContentItem>(contentType, new Type[] { contentType }, iq => iq)) { JObject jObjFrom; if (pathInSummary) { jObjFrom = JObject.Parse(ci.Summary ?? "{}"); } else { jObjFrom = JObject.Parse(ci.Content ?? "{}"); } JObject jObjTo = null; bool doDelete = isDelete ?? false; bool wasComputed = false; if (!doDelete) { if (newPathInSummary) { jObjTo = JObject.Parse(ci.Summary ?? "{}"); } else { jObjTo = JObject.Parse(ci.Content ?? "{}"); } jObjTo.CopyPropertyFrom(newPath, jObjFrom, path); JProperty prop = jObjTo.PropertyByPath(path); // try to deserialize in case its a computed property if (!pathInSummary && newPathInSummary && prop == null) { object content = jObjFrom.ToObject(contentType); object val = ReflectionX.GetPropertyValueByPath(content, path); if (val != null) { string pName = newPath.Contains(".") ? newPath.LastAfter(".") : newPath; jObjTo.AddAtPath(newPath, new JProperty(pName, val)); wasComputed = true; } } if (pathInSummary == newPathInSummary) { if (prop != null) { prop.Remove(); updated++; } else { propMissing++; } } } if (pathInSummary != newPathInSummary || doDelete) // we need to update both summary and content { // remove the old path JProperty prop = jObjFrom.PropertyByPath(path); if (prop != null) { prop.Remove(); updated++; if (pathInSummary) { ci.Summary = jObjFrom.ToString(); } else { ci.Content = jObjFrom.ToString(); } } else if (wasComputed) { updated++; } else { propMissing++; } } if (!doDelete) { if (newPathInSummary) { ci.Summary = jObjTo.ToString(); } else { ci.Content = jObjTo.ToString(); } } repo.Set(new List <object> { ci }, new Dictionary <string, object>()); } return(Content(string.Format("{0} updated {1} had property missing", updated, propMissing))); }
/// <summary> /// Set (create or update) a list of containers to the data source /// </summary> /// <param name="items">The list of containers</param> /// <param name="setOptions">Options for setting</param> /// <returns>List of flags for whether the corresponding by position item was created (rather than updated)</returns> public virtual List <bool> Set(List <object> items, Dictionary <string, object> setOptions) { var db = GetDb(); var createds = new List <bool>(); bool?create = setOptions.Get <bool?>("create"); bool bypassChecks = setOptions.Get <bool>("bypassChecks", false); bool anyUnhandled = false; try { var savedItems = new List <Tuple <object, bool> >(); foreach (object item in items) { var idProp = LinqX.GetIdProp(item.GetType(), this.IdName); bool isAdd; if (create == null) { object noId = ReflectionX.GetDefault(idProp.PropertyType); isAdd = idProp.GetValue(item).Equals(noId); } else { isAdd = create.Value; } var eventData = new RepositoryEventData(item, bypassChecks); var eventResult = EventHub.Instance.ProcessEvent("Repository.Set." + (isAdd ? "Add" : "Update"), this, eventData); var itemSave = ((RepositoryEventData)eventResult.Data).Container; bool wasHandled = ((RepositoryEventData)eventResult.Data).WasHandled; if (!wasHandled) { anyUnhandled = true; } isAdd = eventResult.EventName.EndsWith("Add"); if (isAdd) { DoAdd(db, itemSave, idProp, wasHandled); } else if (!wasHandled) { db.Entry(itemSave).State = EntityState.Modified; } savedItems.Add(Tuple.Create(itemSave, isAdd)); createds.Add(isAdd); } if (savedItems.Count > 0 && anyUnhandled) { db.SaveChanges(); } foreach (var savedItem in savedItems) { var eventData = new RepositoryEventData(savedItem.Item1, bypassChecks); EventHub.Instance.ProcessEvent("Repository.Saved." + (savedItem.Item2 ? "Add" : "Update"), this, eventData); } return(createds); } finally { EndCallDb(db); } }