do.Develop();

this.AreaOfInterest = Development.All();

Dynamically load Entity Configurations in EF CodeFirst CTP5

Dynamically load your EF Code First Configurations is a little more tricky with the new CTP5 since the EntityTypeConfiguration’s base type is not a generic typed class.
In CTP4 EntityConfiguration<> inherited StructuralTypeConfiguration which could easily be checked in a Reflection query.

With CTP5 EntityTypeConfiguration<> (yes, renamed to that) now inherits StructuralTypeConfiguration<TEntityType>. So the Reflection method needs to be a little different.

When you tell your ModelBuilder to add configurations in CTP4 the Add() method accepted a StructuralTypeConfiguration instance.
In CTP5 the Add() method accepts a ComplexTypeConfiguration<TComplexType> or a EntityTypeConfiguration<TEntityType> this will give you a little headache. dynamic to the rescue!

First we search our assembly for EntityTypeConfigurations with Reflection (since this is the startup, don’t bother telling me about the Reflection performance)

//Culture is a class defined in the assembly where my Entity models reside
var typesToRegister = Assembly.GetAssembly(typeof(AnyOfYourConfigurationClassesHere)).GetTypes()
.Where(type => type.Namespace != null && type.Namespace.Equals(typeof(AnyOfYourConfigurationClassesHere).Namespace))
.Where(type => type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));

With each entry in this IEnumerable we call the ModelBinder.Configurations.Add() method, but we use dynamic since we don’t explicitly now the configuration type.

foreach (var type in typesToRegister)
{
dynamic configurationInstance = Activator.CreateInstance(type);
modelBuilder.Configurations.Add(configurationInstance);
}

I think this is a perfect use of both the dynamic keyword aswell as still be able to automatically load the configurations used in your EF CodeFirst project.

About these ads

December 8, 2010 - Posted by | Development, Entity Framework | ,

21 Comments »

  1. This did not seem to work for me.

    Comment by Paul | December 11, 2010 | Reply

    • Why? What is the error?

      Comment by Jonas Cannehag | December 11, 2010 | Reply

      • When I comment out my explicit mapping for each EntityTypeConfiguration class I have with this code:
        protected override void OnModelCreating(ModelBuilder modelBuilder) {
        var typesToRegister = Assembly.GetAssembly(typeof(Culture)).GetTypes()
        .Where(type => type.Namespace != null && type.Namespace.Equals(typeof(Culture).Namespace))
        .Where(type => type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration));
        foreach (var type in typesToRegister)
        {
        dynamic configurationInstance = Activator.CreateInstance(type);
        modelBuilder.Configurations.Add(configurationInstance);
        }
        }

        I get this error that is caused by the mapping file not be added to the modelBuilder:

        Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.

        An error occurred while saving entities that do not expose foreign key properties for their relationships. The GetEntityEntry method will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.

        Comment by Paul | December 12, 2010

      • Can you check what you have in your typesToRegister? How many mapping-files do you have? Can you manually add all of them and see if that solves the error? I don’t think is does since this method is supposed to automatically do that for you. It does not register them in a “special”.

        It may be a problem with one/some of your inherited EntityTypeConfiguration classes since the mapping-logic has changed a lot in CTP5

        Comment by Jonas Cannehag | December 12, 2010

  2. When I add them manually like this:

    modelBuilder.Configurations.Add(new UserMap());

    The site works. I checked and this code:
    var typesToRegister = Assembly.GetAssembly(typeof(Culture)).GetTypes()
    .Where(type => type.Namespace != null && type.Namespace.Equals(typeof(Culture).Namespace))
    .Where(type => type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration));

    Is returning an empty Enumerable. Am I using it wrong?

    Comment by Paul | December 12, 2010 | Reply

    • Hi, maybe it was unclear in the text but the sourcecode had this comment: //Culture is a class defined in the assembly where my Entity models reside

      If you are using this and refer to another Culture class you may be getting it wrong.

      typeof(Culture) should be changed in two places in that code and Culture should be replace by the classname of a EntityTypeConfiguration class in your project. (or any other class that is in the same assembly and namespace as you configurations)

      Hope this helps

      Comment by Jonas Cannehag | December 13, 2010 | Reply

  3. I tried the code with 3 mapping classes contained in an assembly:

    public class CustomerMapping : EntityTypeConfiguration

    CategoryMapping : EntityTypeConfiguration

    public class ProductMapping : EntityTypeConfiguration

    => then an error occurs: “Sequence contains more than one matching element”

    But if I used only one mapping class. e.g. CustomerMapping and excluded CategoryMapping and ProductMaping out of the contained assembly, it’s okay. Strange! Do you have any idea?

    Thanks.

    Comment by huyrua | December 16, 2010 | Reply

    • Hi, as a matter of fact i do since i also ran into the same problem. The problem does not seem to be the way how the configurations are added. It is more a problem with the mapping itself.

      If you manually add them via OnModelCreating() method like this:
      modelBuilder.Configurations.Add(new CustomerMapping());
      modelBuilder.Configurations.Add(new CategoryMapping());
      modelBuilder.Configurations.Add(new ProductMapping());

      i think you will end up with the same error.

      CTP5 makes the whole foreginkey-mapping a little more complex than CTP4 was (at least that is my opinion). Now there is some more quirks you need to put into you mapping to make the relationship work. Otherwise you will end up with the exception you get.

      It is to hard to say without seeing your typeconfigurations but do have a look at the HasForeignKey() method

      Comment by Jonas Cannehag | December 16, 2010 | Reply

  4. The mappings is fine in CTP4. Not sure why it has problem after being upgraded to CTP5.

    I’ll try to solve it out.
    Thanks.

    Comment by huyrua | December 16, 2010 | Reply

    • That is exactly what happened to me. All my mappings worked fine with CTP4 and now my whole project is in a state where i have to redo all my mappings :(

      Comment by Jonas Cannehag | December 16, 2010 | Reply

    • I just found that in CTP5, EF does some magic of automatically mapping the relationships between entity. Check out my post at: http://huyrua.wordpress.com/2010/12/16/entity-framework-4-poco-repository-and-specification-pattern-upgraded-to-ctp5/ to see how the mapping is written.

      Thanks.

      Comment by huyrua | December 16, 2010 | Reply

      • Hi, great post. The only problem i have is that i map to an existing database and the foreignkey’s does not follow EF’s conventions (at least i think so). My foreignkey fields are called [Entity]_Id

        So that is why i think that i need to have a custom mapping for my relations. Still, i will test if it will resolve those automatically.

        Another thing is that i need to mark some of the relations with WillCascadeOnDelete() and that must be done with a full mapping of the property

        Comment by Jonas Cannehag | December 16, 2010

  5. i am using Data annotations instead of mapping classes…how could do then…

    Comment by taher | February 2, 2011 | Reply

    • Hi, if you are using DataAnnotations to annotate your entities you don’t need this automatic loading of configurationclasses. When you add you entities to your DataContext it will resolve the configuration (based on the attributes) automatically. The use of configurationclasses is for me a more structured way of configuring an entity instead of mess it up with all the relations and such in an attributebased manner.

      But of course it depends on how complex your entities configuration gets…

      Comment by Jonas Cannehag | February 2, 2011 | Reply

      • yeah exactly, i wanted to generate my DBContext dynamically

        what i am doing right now is this

        public class MyDbContext:DbContext
        {
        public DbSet product;
        }

        i dont want to add a “public DbSet product” property everytime i add a new entity in my project. i want MyDBContext to be generated automatically, either using annotaions or convention….i personally prefer attributes over mapping classes because i think you have to write twice the amount of code with mapping classes…if i have 30 entities..with mapping i’ll have 60 classes in my project…and attributes do not interfere with my class code,so i think its a more cleaner approach than mapping classes…like aspects in aspect oriented programming :)

        Comment by taher | February 4, 2011

      • There is not need to add properties for each entity. Use the Set() and pass the generic type of the class you want :)

        Comment by Jonas Cannehag | February 4, 2011

  6. like this ?
    public class ProductContext : DbContext
    {

    protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
    {
    Set(typeof(Node));
    }
    }

    but this does not work ..

    Comment by taher | February 4, 2011 | Reply

    • No, if you are putting all your configuration inside your models with DataAnnotations there is not need for you to override OnModelCreating. That is just a method to further extend how your entities are mapped.

      You can remove that method and simply do this:

      var context = new ProductContext();
      var nodes = context.Set<Node>();

      Then put some more queries into that DbSet that is returned or just do a ToList() on it to get a list of Nodes.

      Hope this helps!

      Comment by Jonas Cannehag | February 4, 2011 | Reply

  7. I came so close to getting this right before I found your post. The only thing I was missing was “dynamic”! Thanks for sharing.

    Comment by jrummell | July 25, 2011 | Reply

  8. [...] This adds the custom EntityTypeConfiguration instance to the list of configurations that will be used to build the final model. At this point, we could just reflect over the assembly looking for EntityTypeConfiguration classes, instantiating them, and adding them to the ConfigurationRegistrar (as described by Jonas Cannehag): [...]

    Pingback by Custom Entity Type Configurations in Entity Framework Code First (Part 1) | Somedave | April 17, 2013 | Reply


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: