none
How to force DbContext OnModelCreating triggered before my custom code. RRS feed

  • Question

  • I have EntityFactory class that helps me discovers all the mapping of my entities based on DataAnnotation and FluentAPI using reflection. It works fine and it can discover all mapping on DataAnnotation but for Fluent API, it will only works only after onModelCreating of DbContext was already triggered. How can I make sure that the DbContext was already initialized or force the DbContext to trigger OnModelCreating.

    Ex:

    EntityFactory<StudentEntity>.Properties //-- this gives me all primitive properties of StudentEntity and there database mapping specified either on DataAnnotation or Fluent API.
    

    I know calling one of the DbSet of my DbContext property will  initialized the OnModelCreating but I'm looking for more generic approach.

    Monday, July 18, 2011 11:09 PM

Answers

  • Hello,

    model is created lazily the first time you want to use the context for entity retrieval or for entity persistence. If you want to create the model explicitly you must do it manually:

    DbModelBuilder builder = new DbModelBuilder();
    // Setup configurations
    DbModel model = builder.Build(connection);
    DbCompiledModel compiledModel = model.Compile();
    DbContext context = new DbContext(connection, compiledModel);
    

    This includes additional complexities because this code should be run only once in your application and you should reuse DbCompiledModel for all your instantiated contexts.

    Best regards,
    Ladislav

    Tuesday, July 19, 2011 8:07 AM

All replies

  • Hello,

    model is created lazily the first time you want to use the context for entity retrieval or for entity persistence. If you want to create the model explicitly you must do it manually:

    DbModelBuilder builder = new DbModelBuilder();
    // Setup configurations
    DbModel model = builder.Build(connection);
    DbCompiledModel compiledModel = model.Compile();
    DbContext context = new DbContext(connection, compiledModel);
    

    This includes additional complexities because this code should be run only once in your application and you should reuse DbCompiledModel for all your instantiated contexts.

    Best regards,
    Ladislav

    Tuesday, July 19, 2011 8:07 AM
  • Thanks for reply.

    I was able to create simple solution for my issue by creating extension class for DbSet with all the methods of my EntityFactory class. Inside the extension class, I just call DbSet.ToString() one time to trigger the initialization of my DbContext before I made a call to my EntityFactory methods/properties. This will make sure that the OnModelCreating of context will run first. It's not elegant but works as I intended with very little code.

    Create Extension class for DbSet

     

    public static class DbSetEx
     {
     private static bool _contextInit; 
    
     public static string GetMapColumn<T, P>(this DbSet<T> set, Expression<Func<T, P>> propertySelector) where T : class
     {
      if (!_contextInit) Initialized(set);
      return EntityInfoFactory<T>.GetMapColumn(propertySelector);
     }
    
     public static string GetSelectColumns<T>(this DbSet<T> set, string alias = "", params string[] addCustomColumns) where T : class
     {
      if (!_contextInit) Initialized(set);
      return EntityInfoFactory<T>.GetSelectColumns(alias, addCustomColumns);
     }
    
     public static string GetTableName<T>(this DbSet<T> set) where T : class
     {
      if (!_contextInit) Initialized(set);
      return EntityInfoFactory<T>.TableName;
     }
    
     public static Dictionary<string, PropertyInfo> GetProperties<T>(this DbSet<T> set) where T : class
     {
      if (!_contextInit) Initialized(set);
      return EntityInfoFactory<T>.Properties;
     }
    
    
     private static void Initialized<T>(DbSet<T> set) where T : class
     {
      set.ToString();
      _contextInit = true; 
     }
     }
    

     

    My sample context class:

     

     public class SampleContext : DbContext
     {
     public DbSet<StudentEntity> Students { get; set; }
     
    
     protected override void OnModelCreating(DbModelBuilder modelBuilder)
     {  
      modelBuilder.Configurations.Add(new StudentEntityConfig());
     }
    }
    

     

    Now I can do this:

     

    var context = new SampleContext();
    var firstNameFieldName = context.Students.GetMapColumn(a => a.FirstName);
    


     



    Tuesday, July 19, 2011 6:37 PM
  • Ok, great.

    But if now I have the class LoanAdminContext, inherited from DbContext, how to implement this approach?

    Currently I have 2 constructors:

            static LoanAdminContext() {
    
                // Database schema should not be updated, so initializer is set to null
                Database.SetInitializer<LoanAdminContext>(null);
    
                // Create TraceSource for tracing and debugging
                LoanAdminContext.TraceSource = new TraceSource(typeof(LoanAdminContext).FullName);
            }

    and

            public LoanAdminContext(String nameOfConnectionString)
                : base(nameOfConnectionString) {
    
                // To remove ANSI padding from entities with database columns of fixed size
                ((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += ObjectContext_ObjectMaterialized;
    
                // Create dictionary to hold QueryTranslators for entity types
                this.QueryTranslators = new Dictionary<Type, IQueryable>();
    
                // Create list to hold insertable items
                this.AddedItems = new List<Object>();
    
                // Create list to hold updatable items
                this.UpdatedItems = new List<Object>();
            }
    Besides, I have much of logic withing OnModelCreating method. As far as I understood, using your approach, I may get rid of this method? Where to put its logic then?

    I suspect I should use static Create method, that returns new constructor of LoanAdminContext? How to create DbConnection object then? Now I just use connection string, which contains provider name, too (Oracle Managed Client). In new approach I need to indicate type of connection explicitly, that is not what I want... Or I just miss something.

    Friday, May 29, 2015 10:03 AM
  • Please start a new thread with your question.

    David


    David http://blogs.msdn.com/b/dbrowne/

    Friday, May 29, 2015 1:26 PM