private Dictionary <Type, List <string> > GetSets() { DbContextTypesInitializersPair initializersPair; if (!DbSetDiscoveryService._objectSetInitializers.TryGetValue(this._context.GetType(), out initializersPair)) { ParameterExpression parameterExpression = Expression.Parameter(typeof(DbContext), "dbContext"); List <Action <DbContext> > initDelegates = new List <Action <DbContext> >(); Dictionary <Type, List <string> > entityTypeToPropertyNameMap = new Dictionary <Type, List <string> >(); foreach (PropertyInfo propertyInfo in this._context.GetType().GetInstanceProperties().Where <PropertyInfo>((Func <PropertyInfo, bool>)(p => { if (p.GetIndexParameters().Length == 0) { return(p.DeclaringType != typeof(DbContext)); } return(false); }))) { Type setType = DbSetDiscoveryService.GetSetType(propertyInfo.PropertyType); if (setType != (Type)null) { if (!setType.IsValidStructuralType()) { throw Error.InvalidEntityType((object)setType); } List <string> stringList; if (!entityTypeToPropertyNameMap.TryGetValue(setType, out stringList)) { stringList = new List <string>(); entityTypeToPropertyNameMap[setType] = stringList; } stringList.Add(propertyInfo.Name); if (DbSetDiscoveryService.DbSetPropertyShouldBeInitialized(propertyInfo)) { MethodInfo method1 = propertyInfo.Setter(); if (method1 != (MethodInfo)null && method1.IsPublic) { MethodInfo method2 = DbSetDiscoveryService.SetMethod.MakeGenericMethod(setType); MethodCallExpression methodCallExpression1 = Expression.Call((Expression)parameterExpression, method2); MethodCallExpression methodCallExpression2 = Expression.Call((Expression)Expression.Convert((Expression)parameterExpression, this._context.GetType()), method1, (Expression)methodCallExpression1); initDelegates.Add(Expression.Lambda <Action <DbContext> >((Expression)methodCallExpression2, parameterExpression).Compile()); } } } } Action <DbContext> setsInitializer = (Action <DbContext>)(dbContext => { foreach (Action <DbContext> action in initDelegates) { action(dbContext); } }); initializersPair = new DbContextTypesInitializersPair(entityTypeToPropertyNameMap, setsInitializer); DbSetDiscoveryService._objectSetInitializers.TryAdd(this._context.GetType(), initializersPair); } return(initializersPair.EntityTypeToPropertyNameMap); }
// <summary> // Processes the given context type to determine the DbSet or IDbSet // 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() { DbContextTypesInitializersPair setsInfo; if (!_objectSetInitializers.TryGetValue(_context.GetType(), 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(DbContext), "dbContext"); var initDelegates = new List <Action <DbContext> >(); var entityTypes = new Dictionary <Type, List <string> >(); // Properties declared directly on DbContext such as Database are skipped foreach (var propertyInfo in _context.GetType().GetInstanceProperties() .Where(p => p.GetIndexParameters().Length == 0 && p.DeclaringType != typeof(DbContext))) { var entityType = GetSetType(propertyInfo.PropertyType); if (entityType != null) { // 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); } List <string> properties; if (!entityTypes.TryGetValue(entityType, out properties)) { properties = new List <string>(); entityTypes[entityType] = properties; } properties.Add(propertyInfo.Name); if (DbSetPropertyShouldBeInitialized(propertyInfo)) { var setter = propertyInfo.Setter(); if (setter != null && setter.IsPublic) { var setMethod = SetMethod.MakeGenericMethod(entityType); var newExpression = Expression.Call(dbContextParam, setMethod); var setExpression = Expression.Call( Expression.Convert(dbContextParam, _context.GetType()), setter, newExpression); initDelegates.Add( Expression.Lambda <Action <DbContext> >(setExpression, dbContextParam).Compile()); } } } } Action <DbContext> initializer = dbContext => { foreach (var initer in initDelegates) { initer(dbContext); } }; setsInfo = new DbContextTypesInitializersPair(entityTypes, 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); }