Note: this post is a bit outdated. Checkout this other post for more up to date information on this topic.
Dynamic Data works out of the box with Entity Framework, but it takes a small trick to get it working with the latest EF Code First bits (known as CTP5).
Here is quick walk through of what you need to do.
As a first step, create a new ASP.NET Dynamic Data Entities Web Application. Then, let’s use NuGet to add EF Code First to your project (I never miss a chance to pitch my new product!). We’ll use it with SQL Compact, and also bring in a sample to get started.
Right click on References and choose ‘Add Library Package Reference’ to bring in the NuGet dialog. Go to the Online tab and type ‘efc’ (for EFCodeFirst) in the search box. Then install the EFCodeFirst.SqlServerCompact and EFCodeFirst.Sample packages:
Now we need to register our context with Dynamic Data, which is the part that requires special handling. The reason it doesn’t work the ‘usual’ way is that when using Code First, your context extends DbContext instead of ObjectContext, and Dynamic Data doesn’t know about DbContext (as it didn’t exist at the time).
I will show you two different approaches. The first is simpler but doesn’t work quite as well. The second works better but requires using a new library.
Approach #1: dig the ObjectContext out of the DbContext
The workaround is quite simple. In your RegisterRoutes method in global.asax, just add the following code (you’ll need to import System.Data.Entity.Infrastructure and the namespace where your context lives):
public static void RegisterRoutes(RouteCollection routes) { DefaultModel.RegisterContext(() => { return ((IObjectContextAdapter)new BlogContext()).ObjectContext; }, new ContextConfiguration() { ScaffoldAllTables = true });
So what this is really doing differently is provide a Lambda that can dig the ObjectContext out of your DbContext, instead of just passing the type to the context directly.
And that’s it, your app is ready to run!
One small glitch you’ll notice is that you get this EdmMetadatas entry in the list. This is a table that EF creates in the database to keep track of schema versions, but since we told Dynamic Data to Scaffold All Tables, it shows up. You can get rid of it by turning off ScaffoldAllTables, and adding a [ScaffoldTable(true)] attribute to the entity classes that you do want to see in there.
Another issue is that this approach doesn’t work when you need to register multiple models, due to the way the default provider uses the ObjectContext type as a key. Since we don’t actually extend ObjectContext, all contexts end up claiming the same key.
Approach #2: use the DynamicData.EFCodeFirstProvider library
This approach is simple to use, but just requires getting a library with a custom provider. If you don’t already have NuGet, get it from here.
Then install the DynamicData.EFCodeFirstProvider package in your project:
PM> Install-Package DynamicData.EFCodeFirstProvider 'EFCodeFirst 0.8' already installed. Successfully installed 'DynamicData.EFCodeFirstProvider 0.1.0.0'. WebApplicationDDEFCodeFirst already has a reference to 'EFCodeFirst 0.8'. Successfully added 'DynamicData.EFCodeFirstProvider 0.1.0.0' to WebApplicationDDEFCodeFirst.
After that, this is what you would write to register the context in your global.asax:
DefaultModel.RegisterContext( new EFCodeFirstDataModelProvider(() => new BlogContext()), new ContextConfiguration() { ScaffoldAllTables = true });And that’s it! This approach allows registering multiple contexts, and also fixes the issue mentioned above where EdmMetadatas shows up in the table list.
Hi,
ReplyDeleteI recently posted a question related to exactly this topic. I am not able to make this work and have tried various ways after reading in different articles.
I even tried the method explained by you but still get following error
I am using EF CodeFirst CTP5 with .Net Framework 4.0, Visual studio 2010.
Complete post is at
http://social.msdn.microsoft.com/Forums/en/adonetefx/thread/53fc5d64-5ff3-4e9f-ae7c-795d1eb750d2?outputAs=rss
The member with identity '' does not exist in the metadata collection.
Parameter name: identity
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ArgumentException: The member with identity '' does not exist in the metadata collection.
Parameter name: identity
Source Error:
Line 42: //DefaultModel.RegisterContext(dataModelprovider, new ContextConfiguration() { ScaffoldAllTables = true });
Line 43:
Line 44: DefaultModel.RegisterContext(() => { return ((IObjectContextAdapter)new ONS_DB2Model.ONS_DB2Entities()).ObjectContext; },
Line 45: new ContextConfiguration()
Line 46: { ScaffoldAllTables = true});
I will really appreciate your help...
really helpful thanks david
ReplyDeletethe only thing i found was that i had to install sqlcompact via the package manager console as it contains powershell scripts - other than that it worked like a charm thank you
@Dave: ah yes, this is something we fixed in NuGet 1.1, which we haven't quite released yet. With 1.0, using the Console is the way to go for this package.
ReplyDeleteDoes it works with many to many relashionships now? Last time I tried (some month ago) they didn't play nice together.
ReplyDelete@guillaume: hmmm, I did not try that. What issue were you running into? You may want to post to the Dynamic Data forum to make it easier to discuss.
ReplyDeleteme again :-)
ReplyDeleteis there any way i can use multiple models only i've got an mvc web ptroject and am using areas to isolate the functionality and when i try to register the second model I get an "Server Error in '/' Application.
Item has already been added. Key in dictionary: 'System.Data.Objects.ObjectContext' Key being added: 'System.Data.Objects.ObjectContext' " exception? I tried using various overloads of the registercontext method (eg not the factory ones) then i get the exception "The context type 'namespace.ContextDerivedClass' is not supported."
I'm sure its possible but think i must be doing something wrong?
Thanks in advance
Dave
@Dave: You're right, I think there is a bug there that we need to look into.
ReplyDeleteThanks David, I tried all sorts of workarounds but none of them work :-(
ReplyDelete@Dave: I just updated the post with a whole new way to make this work. Please let me know how that works out for you.
ReplyDeleteHi David. This is great thank you. Is the source code available? I tried going down the datamodelprovider route but couldn't find any documentation on it? Im guessing its somekind of internal dictionary, am i close?
ReplyDelete@Dave: for the most part, it's a copy of the EF provider that comes with DD, with some small changes. At this time, I can't share the sources, but we are working on making this possible, hopefully within a couple weeks. When that happens, I'll update the post with a link to the sources. In the mean time, you can use reflector if you're curious :)
ReplyDeleteD'oh of course, should have tried that - very long day today. Thanks again
ReplyDeleteDavid, are there updated instructions for doing this with the official EF4.1 release? I have tried both approaches to no avail. My Dynamic Data project was working fine with EF4.0, but I want to take advantage of the new DBContext API in 4.1 (I am sticking with DB First however). Please advise?
ReplyDelete1. I initially tried the EFCodeFirstProvider approach, but found the version on NuGet pulls in CTP5 which conflicts with 4.1
2. I then tried approach #1, but I get the following exception. Note that "Ticker" is one of my entity types which is defined in an entity model defined in a shared lib referenced by my web app project. The correct qualified name for the type is "MmmSharedLib.Ticker".
System.ArgumentException was unhandled by user code
Message=Could not find the CLR type for 'MmmModel.Ticker'.
Source=System.Data.Entity
StackTrace:
at System.Data.Metadata.Edm.MetadataWorkspace.GetObjectSpaceType(StructuralType edmSpaceType)
at System.Web.DynamicData.ModelProviders.EFDataModelProvider.GetClrType(EntityType entityType)
at System.Web.DynamicData.ModelProviders.EFDataModelProvider.CreateTableProvider(EntitySet entitySet, EntityType entityType)
at System.Web.DynamicData.ModelProviders.EFDataModelProvider..ctor(Object contextInstance, Func`1 contextFactory)
at System.Web.DynamicData.ModelProviders.SchemaCreator.CreateDataModel(Object contextInstance, Func`1 contextFactory)
at System.Web.DynamicData.MetaModel.RegisterContext(Func`1 contextFactory, ContextConfiguration configuration)
at SitefinityWebApp.Global.RegisterRoutes(RouteCollection routes) in C:\Documents and Settings\Adam\My Documents\MMM\Dev\Sitefinity\TestProj2\Global.asax.cs:line 40
at SitefinityWebApp.Global.Application_Start(Object sender, EventArgs e) in C:\Documents and Settings\Adam\My Documents\MMM\Dev\Sitefinity\TestProj2\Global.asax.cs:line 73
InnerException:
I just updated the DynamicData.EFCodeFirstProvider package to work with the newer EntityFramework.
ReplyDeleteHello,
ReplyDeleteI'm using your package with CF context in which I have overriden SaveChanges, but it does not get called. It does not happen with first approach neither, DD takes base object and ignores my implementation. I suppose there is no way to get this working?
@obrad: you're right, this does seem broken. I think it's because we extract the ObjectContext out of the DbContext and then work with that, leaving the DbContext unused. I'd need to talk with EF team to see if there is a way around.
ReplyDelete@obrad: dev from EF team wrote "This overridden SaveChanges was the one gotcha with that approach. You can use the “SavingChanges” event on the ObjectContext to do some of your pre-save logic, but there isn’t a way to do any post-save logic unless there was some callback they could hook into in dynamic data that was post save".
ReplyDeleteMade pre-save logic is what you need anyway?
I'm getting "Item has already been added" exception on debugging my asp.net dynamic data entities web application at this line,
ReplyDeleteXModel.RegisterContext(typeof(XModel.XEntities), new ContextConfiguration() { ScaffoldAllTables = true });
What could be the reason? And, how to address?
I do not get this error while running the app for the first time...
@Anand: I'd need to see more info, like a complete stack trace. Can you post a question to StackOverflow with more details? Blog comments get messy quickly when we need to go back and forth :)
ReplyDelete@David: I have posted the question in Stackoverflow http://stackoverflow.com/questions/7851440/exception-item-has-already-been-added-in-metamodel-register. Your inputs on resolving this is much appreciated..Thanks...sorry for posting this info in this blog...
ReplyDelete@Anand: no problem, I answered it there.
ReplyDeleteThis is great. I've been trying for two days to add MVC 3 functionality to this also. Can you give me an idea of what the best way to do that would be?
ReplyDelete@Ben: I'm not sure I understand how MVC3 relates to this. Can you be more specific?
ReplyDelete@David: I'm sorry, it might be unrelated. I'm not very experienced w/ .NET. I think it is great that code first works with dynamic data. I was trying to reinvent it in MVC. But now I would just like to use MVC for custom views and dynamic data with the rest. I'm trying to figure out how to combine the two in a single app using the same code first model. It is painful teaching myself, but maybe it is an unrelated challenge. On another subject, would you still recommend a custom MetaModel for dealing w/ complex types in Dynamic Data? Will DD ever support complex types? Thanks.
ReplyDelete@Ben: I would suggest posting the various questions to the Dynamic Data forum (http://forums.asp.net/1145.aspx) if they don't directly relate to the post. Blog comments just don't work well for general discussion, as no one else from the community sees it here. :)
ReplyDeleteActually, if you don't mind, I thought of another and probably more important question. Does Dynamic Data still work with Code First when using EntityFramework.Migrations? Thanks again.
ReplyDelete@Ben: yes, it should still work. In fact, the NuGet gallery (https://github.com/NuGet/NuGetGallery) is doing that. It uses migrations, and uses DD for its admin UI. Which might actually help with your other question about mixing MVC and DD :)
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteHi, just in case someone wants to use Dynamic Data with EF v4.2... The second approach worked for me but I've also had to redirect the EF v4.1 to the EF v4.2 in the web.config. More info on how to do that in MSDN (http://msdn.microsoft.com/en-us/library/7wd6ex19%28v=vs.100%29.aspx)
ReplyDeleteDavid thanks again for your help. I ended up going with MvcScaffolding for the admin UI after I learned that I could foreach the scaffolding process right from the package manager console. It is a little slow when doing 50+ classes and requires playing with the T4 if you want to make changes but the performance seemed better than DD in the end. It would be cool to see T4 templates for MVC scaffolding in a similar structure to the Dynamic Data templates.
ReplyDeletei have issue with the Dynamic Data Server Error with Entity Framework 4.3 and i have gone through the below thread and here i have found the solution. so if anyone have issue with Dynamic Data Server Error with Entity Framework 4.3 then you go through here:
ReplyDeletehttp://forums.techarena.in/software-development/1460986.htm
Hello, I ran into an issue when using Dynamic Data with EF 5.x DBContext generated classes and ManyToMany_Edit.ascx Dynamic Data Field Template, I order to make the Dynamic Data Field you need to change the code in ManyToMany_Edit.ascx.vb from :
ReplyDelete' Go through all the territories (not just those for this employee)
For Each childEntity As Object In childTable.GetQuery(objectContext)
Dim actualTable As MetaTable = MetaTable.GetTable(childEntity.GetType())
' Create a checkbox for it
Dim listItem As New ListItem(actualTable.GetDisplayString(childEntity), actualTable.GetPrimaryKeyString(childEntity))
' Make it selected if the current employee has that territory
If Mode = DataBoundControlMode.Edit Then
listItem.Selected = entityList.Contains(childEntity)
End If
CheckBoxList1.Items.Add(listItem)
Next
To this one:
' Go through all the territories (not just those for this employee)
For Each childEntity As Object In childTable.GetQuery() ' GetQuery(objectContext)
Dim actualTable As MetaTable = MetaTable.GetTable(childEntity.GetType().BaseType)
' Create a checkbox for it
Dim listItem As New ListItem(actualTable.GetDisplayString(childEntity), actualTable.GetPrimaryKeyString(childEntity))
' Make it selected if the current employee has that territory
If Mode = DataBoundControlMode.Edit Then
listItem.Selected = entityList.Contains(childEntity)
End If
CheckBoxList1.Items.Add(listItem)
Next
Hope it Helps
Regards,
Cédric
Hello David!
ReplyDeleteOn my search to solve my problem with registering multiple models I found your homepage. I downloaded the sources and compiled it with EntityFramework 5.
When I run my code, I get now always the error:
The context type 'DynamicData.EFCodeFirstProvider.EFCodeFirstDataModelProvider' is not supported.
When I switch the registration back how it was originally
metaModel.RegisterContext(
() => { return ((IObjectContextAdapter)MyContext()).ObjectContext; },
...);
it works, but only for one model. Can you please help me? I am stuck...
Thanks,
Stefan