private Dictionary <Type, List <string> > GetSets() { DbContextTypesInitializersPair setsInfo; if (!_objectSetInitializers.TryGetValue(_context.GetType(), out setsInfo)) { var dbContextParam = Expression.Parameter(typeof(DbContext), "dbContext"); var initDelegates = new List <Action <DbContext> >(); var entityTypes = new Dictionary <Type, List <string> >(); 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) { if (!entityType.IsValidStructuralType()) { throw new InvalidOperationException(entityType.Name); } List <string> properties; if (!entityTypes.TryGetValue(entityType, out properties)) { properties = new List <string>(); entityTypes[entityType] = properties; } properties.Add(propertyInfo.Name); 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); _objectSetInitializers.TryAdd(_context.GetType(), setsInfo); EntitySetCache.Initialize(entityTypes); } return(setsInfo.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; }