EF6: Nested types and more for Code First

Ever since POCO support was introduced in EF4 there have been two limiting restrictions on the types that can be mapped:

  • Types nested inside other types were not supported
  • Types were matched by simple names only

Some recent check-ins to the EF6 code base have lifted these restrictions to some degree when using Code First. This post describes what is now supported, what restrictions still exist, and provides some background about why things are the way they are.

Note that these changes did not make it into EF6 alpha 3, but they are in recent nightly builds.

What is now supported?

In a nutshell, the changes described below mean that:

  • Types not used in a Code First model but with the same simple name as types in the model will not cause EF to throw. For example, you can now have DTO types and entity types with the same names in the same assembly.
  • Nested types can be used.

Note that “simple name” means the name of the type excluding its namespace or any outer type names. The simple name of “My.Namespace.MyType” is “MyType” and the simple name of the nested type “My.Namespace.OuterType+InnerType” is “InnerType”.

What restrictions still exist?

Every type used in a given Code First model must still have a unique simple name. That is, using “My.Namespace1.Product” and “My.Namespace2.Product” in the same Code First model is not supported. Both these types can exist in the same assembly, and both can be used in different Code First models, or one can be used by Code First and the other not, but they can’t both be used by the same Code First model.

Also, these changes only apply to Code First. At the time of writing Database First and Model First still have the restrictions on types that they always had.

See Future Plans below for ideas on how we plan to remove these remaining restrictions in a future release of EF.

Background

Consider starting an EF application that contains a model in an EDMX file. EF reads the model from the EDMX file and then must find the CLR types that should be used with this model. For POCO entities this is done by scanning through assemblies that might contain the types and looking for matches. Matches are made by simple type name only—that is, there is no concept of matching namespaces or nested types. An exception is thrown if EF finds two types that have the same simple name and are therefore both potential matches.

This is clearly not a very good way of doing things even though arguments were made that it had to be this way. I won’t say any more about the decision to do it this way; right or wrong it is what it is.

Code First arrives

With Code First the CLR types that should be used do not need to be discovered—they are the “code” in Code First from which the model is created. So, theoretically, the above process should not have been needed for Code First. The problem was that until EF6 Code First was in a separate assembly to core EF with no access to internals. It therefore worked by creating an EDMX in memory and pushing that to core EF, which then loaded the EDMX and was forced to discover types again using the process described above.

What changed?

With the move out of the .NET Framework for EF6, Code First and the core code are now all in EntityFramework.dll. This has allowed what we refer to as “metadata unification”. That is, Code First now doesn’t create an EDMX in memory, but rather creates runtime metadata directly. (At the time of writing the MSL part of the EDMX is still created as XML because its validation at runtime is tightly coupled to reading this data from XML.)

This metadata unification was a very significant and non-trivial task, but now that it has been done it opens the door for other changes. (One of these is stored proc mapping in Code First—one of the reasons this is coming late in the EF6 cycle is because it makes use of this metadata unification.)

With metadata unification Code First now annotates the model it builds with CLR type information. This type information can then be used directly by the runtime so that for Code First the process described above is no longer needed. This means that:

  • Nested types work with Code First because the discovery mechanism that doesn’t understand them is no longer used
  • Types not used in the Code First model don’t have any impact because core EF no longer needs to try to find the correct types to use

Future plans

As stated above, every type used in a given model must still have a unique simple name. This is because every type in the conceptual model (the CSDL part of an EDMX) is still included in the same conceptual model namespace. The conceptual model (and in particular the CSDL XML schema) is not setup to deal well with types in the same model but in different namespaces. However, this is something we want to tackle post EF6 such that EF can use any number of types with the same simple name so long as they have different namespaces or outer types.

We also plan to change the way type discovery happens for Database First and Model First when an EDMX file is loaded. It’s not clear exactly how this will be done, but the most promising approach seems to be to use DbSet/ObjectSet entity type information from the context coupled with the model shape in order to discover the CLR types to use. This should effectively remove the assembly scanning and simple name matching that Database First and Model First currently use. (And we can then hopefully dump this code entirely from EntityFramework.dll.)

It’s worth noting that using DbSet/ObjectSet information could break those using Database First or Model First without using DbSets or ObjectSets. It’s not clear that anybody is doing this, but if you are now would be a great time to contact the EF team!

About Arthur Vickers

Developer on the Entity Framework team at Microsoft.
This entry was posted in Code First, Entity Framework and tagged , , . Bookmark the permalink.

2 Responses to EF6: Nested types and more for Code First

  1. Its always good to know more about EF and its progress

  2. Calvin Craig says:

    It is a pity that the EF code-first model builder doesn’t (yet) allow inclusion of two classes having the same simple name and scoped by different namespaces. For example, I have a frequently used class/entity named “Type” that is found within forty or so database schemas. And now that I am (finally) moving to EF’s code-first approach, I have put these Type tables into separate C# namespaces which are named to match their SQL Server schemas.

    Incidentally, prior to EF6, the very misleading error message indicates that a type is not mapped when in fact it is mapped. For example,

    EF6: “The type ‘ConMet.Catalog.DomainClasses.PartScope.Type’ and the type ‘ConMet.Catalog.DomainClasses.DrawingBorderScope.Type’ both have the same simple name of ‘Type’ and so cannot be used in the same model.” which helps a lot.

    EF5: “The type ‘ConMet.Catalog.DomainClasses.DrawingBorderScope.Type’ was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive, nested or generic, and does not inherit from EntityObject.”

    It’s also a pity that an entity having the same name as its container is not allowed. For example, a namespace called “HubMachining” containing an entity class also called “HubMachining” doesn’t work. But it would be nice if it did work. I have many [namespace/shema – entity/table] pairs where I would like to use the same names. I have appended “Scope” to my C# namespaces to work around this limitation.

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