9

I noticed that Entity Framework still has a lot of "automagical" features in their latest release. As always, this is a truly double-edged sword.

Specifically, I'm using the OnModelBuilder event to create my model on the fly in code using the fluentAPI (http://msdn.microsoft.com/en-us/library/hh295844(v=vs.103).aspx). I have a large set of entities, and they don't all comply with the Microsoft "standards". For example, my ID columns are named Person_id instead of PersonId. As such, Entity doesn't always auto-detect the primary key on a table, or at least, it doesn't seem to do so.

I don't mind being explicit when building the model, but what does trouble me is that I'm not always sure what properties and relationships Entity will auto-detect and which ones it will erroneously ignore or misidentify. Since most of my entities also have a partial class with helper methods and properties (stuff to handle enums, etc), I greatly fear that someday Entity will auto create mappings between things which shouldn't be mapped (the failure could be Entity or some unsuspecting programmer).

Is there a way I can disable Entity's auto-relationship-hookup feature so that I can be 100% explicit in my OnModelBuilder method? Or, at minimum, how can I know when I need to add extra mapping details (like needing to declare a field optional, or when a specific navigation property won't be autodetected)?

Thanks!

Brett
  • 4,066
  • 8
  • 36
  • 50
  • if this stuff worries you, you should probably use the "design first" aspects of EF rather than the newer "code first" stuff – Robert Levy Nov 30 '11 at 19:47
  • I started with the EDMX, incidentally. I was trying to 'upgrade' to code-first thinking that it might be easier to manage since it'd all be in the code, but it may not be worth the effort and time debugging. – Brett Nov 30 '11 at 19:53

5 Answers5

6

The auto-magic is done by conventions inside EF code first. You can remove any of those conventions to turn off some magic or you can remove them all and after that you will have to be 100% explicit in your fluent-API.

Community
  • 1
  • 1
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • With EF6 removing all the conventions does not help...see my answer and code snippet posted at the end. – baHI Mar 06 '17 at 20:05
1

Ok, since the removing of conventions does not work, there's a simple way to not-to map all non-configured properties inside EF6.x

The basic is to use separate mapping classes, and after doing the manual maps inside a class, simply call a method, that with reflection will force to ignore all properties that weren't configured.

Here's a link to my gist, where the implementation is. Also added a sample as a comment: https://gist.github.com/hidegh/36d92380c720804dee043fde8a863ecb

baHI
  • 1,510
  • 15
  • 20
  • This should work for all properties, so also for collections (1:n) or other references (n:1) – baHI Mar 07 '17 at 16:54
0

Actually this code removes all conventions, even clears the initial set...

...but still columns not mapped with EntityTypeConfiguration are mapped...

private void RemoveAllConventions(DbModelBuilder modelBuilder)
        {
            new List<string>()
            {
            "_configurationConventions",
            "_conceptualModelConventions",
            "_conceptualToStoreMappingConventions",
            "_storeModelConventions"
            }
            .ForEach(field =>
            {
                var values =
                    (IEnumerable<IConvention>)typeof(ConventionsConfiguration)
                        .GetField(field, BindingFlags.Instance | BindingFlags.GetField | BindingFlags.NonPublic)
                        .GetValue(modelBuilder.Conventions);

                modelBuilder.Conventions.Remove(values.ToArray());
            });

            var initialCS = typeof(ConventionsConfiguration)
                        .GetField("_initialConventionSet", BindingFlags.Instance | BindingFlags.GetField | BindingFlags.NonPublic)
                        .GetValue(modelBuilder.Conventions);

            new List<string>()
            {
            "ConfigurationConventions",
            "ConceptualModelConventions",
            "ConceptualToStoreMappingConventions",
            "StoreModelConventions"
            }
           .ForEach(field =>
           {
               var values =
                   (IEnumerable<IConvention>) initialCS
                        .GetType()
                       .GetProperty(field, BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.Public)
                       .GetValue(initialCS);

               modelBuilder.Conventions.Remove(values.ToArray());
           });


        }
baHI
  • 1,510
  • 15
  • 20
  • 1
    It isn't really clear if the code posted answers the question. Is this your current state of things, but it still doesn't work? Or does this solve the issue, but leads to other questions? – Tunaki Mar 06 '17 at 20:27
0

Just be explicit on your relationships, keys and columns in your entities configuration and it really shouldn't be an issue.

I personally, while coding, let it assume until it breaks and then I correct the configuration.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    Database.SetInitializer<AccountWriteContext>(null);

    modelBuilder.Configurations.Add(new AccountEntityTypeConfiguration());
    modelBuilder.Configurations.Add(new AccountOwnerEntityTypeConfiguration());
    modelBuilder.Configurations.Add(new CreditCardEntityTypeConfiguration());
    modelBuilder.Configurations.Add(new TenantEntityTypeConfiguration());
    //blah blah blah
}


class AccountOwnerEntityTypeConfiguration
     : EntityTypeConfiguration<AccountOwner>
{
    public AccountOwnerEntityTypeConfiguration()
    {
        this.HasKey(p => p.ID);
        this.Property(p => p.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).IsRequired();

        this.HasRequired(o => o.CreditCard).WithMany().HasForeignKey(c => c.CreditCardID).WillCascadeOnDelete(true);

        this.ToTable("AccountOwner", "AccountWrite");
    }
}
poke
  • 369,085
  • 72
  • 557
  • 602
William
  • 1,375
  • 12
  • 27
  • This never worked like it is written. Even in that exact example, if I were to go into the AccountOwner entity and add a property, EF would automagically add the property in the next migration even if I don't add it in the EntityTypeConfiguration. – Suamere Mar 29 '17 at 01:28
  • in 2011 it did. – William Feb 26 '18 at 17:01
-3

I would recommend using the Data Annotation Attributes to do this. For example, the [key] attribute can be used to define your primary key, and the [table] attribute can be used to provide the name of your table. The [required] attribute can be used to tell EF that a field is required.

In my opinion, it's much easier to use the attribute syntax than it is to use the fluent syntax to update the model, and it's also self-documenting, since the attributes are placed directly into the object code.

For more info, see this blog post that lists all of the available attributes:

http://blogs.msdn.com/b/efdesign/archive/2010/03/30/data-annotations-in-the-entity-framework-and-code-first.aspx

Roger
  • 269
  • 2
  • 12
  • The question was "how to disable". And I assume without explicitly telling which columns to disable. It's irrelevant whether you prefer annotations or fluent syntax. The goal is to have a stable mapping, without the need to explicitly tell to ignore new properties... – baHI Mar 07 '17 at 09:09