// <summary> // Processes the given context type to determine the EntityRepository or IRepository // properties and collect root entity types from those properties. Also, delegates are // created to initialize any of these properties that have public setters. // If the type has been processed previously in the app domain, then all this information // is returned from a cache. // </summary> // <returns> A dictionary of potential entity type to the list of the names of the properties that used the type. </returns> private Dictionary <Type, List <string> > GetSets() { EntityContextTypesInitializersPair setsInfo; var contextType = _context.GetType(); if (!_objectSetInitializers.TryGetValue(contextType, out setsInfo)) { // It is possible that multiple threads will enter this code and create the list // and the delegates. However, the result will always be the same so we may, in // the rare cases in which this happens, do some work twice, but functionally the // outcome will be correct. var dbContextParam = Expression.Parameter(typeof(EntityContext), "dbContext"); var initDelegates = new List <Action <EntityContext> >(); var typeMap = new Dictionary <Type, List <string> >(); var injection = _service.Provider.GetService <IInjectionProvider>(); // Properties declared directly on DbContext such as Database are skipped foreach (var propertyInfo in contextType.GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.GetIndexParameters().Length == 0 && p.DeclaringType != typeof(EntityContext))) { var entityType = GetSetType(propertyInfo.PropertyType); if (entityType != null) { EntityProxyManager.CompileAll(entityType.Assembly, injection); // We validate immediately because a DbSet/IDbSet must be of // a valid entity type since otherwise you could never use an instance. if (!entityType.IsValidStructuralType()) { //throw Error.InvalidEntityType(entityType); } if (!typeMap.TryGetValue(entityType, out List <string> properties)) { properties = new List <string>(); typeMap[entityType] = properties; } properties.Add(propertyInfo.Name); var setter = propertyInfo.GetSetMethod(); if (setter != null && setter.IsPublic) { var setMethod = MthSetRep.MakeGenericMethod(entityType); Expression expression = Expression.Call(dbContextParam, setMethod); var pType = setter.GetParameters()[0].ParameterType; if (pType != expression.Type) { expression = Expression.Convert(expression, pType); } var setExp = Expression.Call( Expression.Convert(dbContextParam, contextType), setter, expression); #if !NET35 var createExp = Expression.Call(Expression.Constant(_service), MthTryCreateRep, Expression.Constant(entityType)); var blockExp = Expression.Block(setExp, createExp); initDelegates.Add( Expression.Lambda <Action <EntityContext> >(blockExp, dbContextParam).Compile()); #else initDelegates.Add( Expression.Lambda <Action <EntityContext> >(setExp, dbContextParam).Compile()); #endif } } } Action <EntityContext> initializer = dbContext => { foreach (var initer in initDelegates) { initer(dbContext); } }; setsInfo = new EntityContextTypesInitializersPair(typeMap, initializer); // If TryAdd fails it just means some other thread got here first, which is okay // since the end result is the same info anyway. _objectSetInitializers.TryAdd(_context.GetType(), setsInfo); } return(setsInfo.EntityTypeToPropertyNameMap); }
// <summary> // Processes the given context type to determine the EntityRepository or IRepository // properties and collect root entity types from those properties. Also, delegates are // created to initialize any of these properties that have public setters. // If the type has been processed previously in the app domain, then all this information // is returned from a cache. // </summary> // <returns> A dictionary of potential entity type to the list of the names of the properties that used the type. </returns> private EntityContextTypesInitializersPair GetSets() { var contextType = _context.GetType(); return(_objectSetInitializers.GetOrAdd(contextType, k => { var watch = Stopwatch.StartNew(); // It is possible that multiple threads will enter this code and create the list // and the delegates. However, the result will always be the same so we may, in // the rare cases in which this happens, do some work twice, but functionally the // outcome will be correct. var dbContextParam = Expression.Parameter(typeof(EntityContext), "dbContext"); var initDelegates = new List <Action <EntityContext> >(); var typeMap = new Dictionary <Type, List <string> >(); var reposMaps = (from s in k.GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.GetIndexParameters().Length == 0 && p.DeclaringType != typeof(EntityContext)) let entityType = GetSetType(s.PropertyType) where entityType != null select new EntityRepositoryTypeMapper(s, entityType)).ToList(); _options.Initializers?.PreInitialize(new EntityContextPreInitializeContext(_context, _contextService, reposMaps)); // Properties declared directly on DbContext such as Database are skipped foreach (var m in reposMaps) { // We validate immediately because a DbSet/IDbSet must be of // a valid entity type since otherwise you could never use an instance. if (!m.EntityType.IsValidStructuralType()) { //throw Error.InvalidEntityType(entityType); } if (!typeMap.TryGetValue(m.EntityType, out List <string> properties)) { properties = new List <string>(); typeMap[m.EntityType] = properties; } properties.Add(m.Property.Name); var setter = m.Property.GetSetMethod(); if (setter != null && setter.IsPublic) { var setMethod = MethodCache.SetRepository.MakeGenericMethod(m.EntityType); Expression expression = Expression.Call(dbContextParam, setMethod); var pType = setter.GetParameters()[0].ParameterType; if (pType != expression.Type) { expression = Expression.Convert(expression, pType); } var setExp = Expression.Call( Expression.Convert(dbContextParam, contextType), setter, expression); initDelegates.Add( Expression.Lambda <Action <EntityContext> >(setExp, dbContextParam).Compile()); } } void initializer(EntityContext dbContext) { foreach (var initer in initDelegates) { initer(dbContext); } } Tracer.Debug($"The repositories of {contextType.Name} was initialized ({watch.ElapsedMilliseconds}ms)."); return new EntityContextTypesInitializersPair(typeMap, initializer); })); }