Simple Code First with Entity Framework 4 - Magic Unicorn Feature CTP 4
Microsoft's been releasing a number of right-sized LEGO pieces lately. In case you missed it, Betas have been announced for:
- SQL 4 Compact Edition - It has a small embedded file-based SQL Database, and a web-server called IIS Express that's compatible with the full version of IIS.
- "Razor" Page Syntax - A way to make pages/ViewEngine called "Razor." Your sites can be later be expanded to use all of ASP.NET MVC. It's a simple syntax that is easy to learn.
- WebMatrix - It's a small (15 megs if you have .NET 4, 50megs if you don't) lightweight IDE for making ASP.NET or PHP websites. Good for non-pro developers.
- It uses the WebDeploy engine to deploy apps to hosts, setting up permissions, copying databases, etc.
- WebMatrix also has the free Search Engine Optimization Toolkit built in, so you can spider your own site and see how Search Engines see it. It'll make recommendations and store reports.
- IIS Express - A version of the IIS 7.5 Web Server that can be run as non-admin, isn't installed as a service, that will also integrate with Visual Studio
More details to come on all this. Howver, on the tooling side, I did get a chance to talk to Damian Edwards, a developer working on some of this stuff and I put video up on Channel 9 yesterday.
There's lots of cool pieces that are packaged up with WebMatrix initially, but these pieces are interesting for pro developers as well.
Still, something's missing.
In my mind, it's been too hard to talk to databases. I like LINQ to SQL and used it on the first NerdDinner version, but since EF4 sucks so much less is way better than earlier versions of EF, Jon and I updated NerdDinner to use EF. It was easy, but I would have liked to code first, and code only if I could.
Microsoft announced a new Entity Framework CTP today. It has the romantic and wonderful name "Microsoft ADO.NET Entity Framework Feature CTP4" which is lame. You can say "EF Feature CTP4" but I like "EF Magic Unicorn Edition" but that's just me. We're getting the tech better at Microsoft but still can't get naming right. Whadayagonnado? Still, it makes EF a pleasure.
It's got a lot of interesting features and choices, and while it's still a CTP, you should take a minute and check it out.
To get a more detailed version of this walkthrough plus downloadable sample code, check out the ADO team's excellent blog post.
Quick CRUD via a Code First Model
After you install it (it won't mess up your system if you do), go and create a new whatever project. For my little example, I'll make a new ASP.NET MVC Website. It works for me better than a console app to illustrate a point.
Add a reference to Microsoft.Data.Entity.CTP.dll.
Make a new class, maybe in the Models folder, and name it something like Book. Add some code like this. Notice it's just code. Nothing derives from anything.
public class Book
{
[Key]
public int ISBN { get; set; }
[Required]
public string Title { get; set; }
public DateTime FirstPublished { get; set; }
public bool IsFiction { get; set; }
}
Notice I've put [Key] and [Required] on this class, but if that bothers me, I could put these kinds of declarations in a more fluent way in my database context class, in OnModelCreating.
builder.Entity<Book>().HasKey(b => b.ISBN);
builder.Entity<Book>().Property(b => b.Title).IsRequired();
To access my data, Here's a SimpleBookCatalog...
public class SimpleBookCatalog : DbContext
{
public DbSet<Book> Books { get; set; }
}
Next, I'll make a new Controller, via right|click Add Controller. I'll make a BookController.
public class BookController : Controller
{
SimpleBookCatalog db = new SimpleBookCatalog();
public ActionResult Index()
{
var books = db.Books;
return View(books);
}
...
}
I'll right click on the Index method and make an Index view. Then I'll run my app.
No data. What if I look in my SQL Management Studio? I got a Database created for me with a convention-based name since I didn't supply one.
If I specified a different connection string, that DB could be anywhere.
However, if use a different database provider, like say, a SQL 4 Compact Edition one, setting it as the default in my Application_Start:
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
Then when I run my app and look in my App_Data folder:
So I got a file based database without doing anything and I don't need SQL Server. (Yes, I can change the name, location, etc.) If I do nothing, I get a reasonable convention.
Next, I'll add two Create methods, one for a GET and one for a POST. In Create, I'll add my new book and save the changes:
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Book book)
{
db.Books.Add(book);
db.SaveChanges();
return RedirectToAction("Index");
}
I'll right click, Add View, and make a Create View. Run my app, look at the empty list, then click Create.
Click Create, and I'm redirected back to the Index page:
Back on the Index page, I can change the link to Details to use our primary key:
<%: Html.ActionLink("Details", "Details", new { id=item.ISBN })%> |
Create a Details View and add a Details method:
public ActionResult Details(int id)
{
var book = db.Books.Find(id);
return View(book);
}
See that Find method? That's there automatically. I can certainly use al the LINQy goodness as well, but as you can see, CRUD is simple. I can hook up Edit and Delete in a few minutes as well.
Here's the whole thing:
public class BooksController : Controller
{
SimpleBookCatalog db = new SimpleBookCatalog();
public ActionResult Index()
{
var books = db.Books;
return View(books);
}
// GET: /Books/Details/5
public ActionResult Details(int id)
{
var book = db.Books.Find(id);
return View(book);
}
// GET: /Books/Create
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Book book)
{
db.Books.Add(book);
db.SaveChanges();
return RedirectToAction("Index");
}
// GET: /Books/Edit/5
public ActionResult Edit(int id)
{
return View(db.Books.Find(id));
}
// POST: /Books/Edit/5
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var book = db.Books.Find(id);
UpdateModel(book);
db.SaveChanges();
return RedirectToAction("Index");
}
// GET: /Books/Delete/5
public ActionResult Delete(int id)
{
var book = db.Books.Find(id);
return View(book);
}
// POST: /Books/Delete/5
[HttpPost]
public ActionResult Delete(int id, FormCollection collection)
{
db.Books.Remove(db.Books.Find(id));
db.SaveChanges();
return RedirectToAction("Index");
}
}
So that's a nice simple controller that uses a model that was written in just code. The database and its schema was created for me. The DbContext is LINQable with stuff like Add, Find, and Remove all just there. Plus, it's all EF under the hood, so if you need more complex stuff, you can do it.
For example, here's a more complex Code First Model with Collections, and more attributes. I show some fluent wiring up of relationships later on, although there are conventions that can assign bi-directionality based on naming.
public class Book
{
[Key]
public int ISBN { get; set; }
[Required]
public string Title { get; set; }
[Required]
public DateTime FirstPublished { get; set; }
[Required]
public bool IsFiction { get; set; }
public virtual Publisher Publisher { get; set; }
[RelatedTo(RelatedProperty="Author")]
public virtual Author Author { get; set; }
}
public class Person
{
[ScaffoldColumn(false)]
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Author : Person
{
[ScaffoldColumn(false)]
public int AuthorId { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
public class Publisher
{
[ScaffoldColumn(false)]
public int PublisherId { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
public class SimpleBookCatalog : DbContext
{
public DbSet<Book> Books { get; set; }
public DbSet<Person> People { get; set; }
public DbSet<Author> Authors { get; set; }
public DbSet<Publisher> Publishers { get; set; }
}
Also, "Magic Unicorn EF" supports DataAnnotations (or validation via Fluent interfaces), so those [Required] and [StringLength] stuff from before? Those apply not only in JavaScript, but also at the Server-side and Database persistence layers.
You can make your own strategies for creating databases, based on what's going on with the model, if it's changed, etc. Here's some built-in examples. Yours can do whatever you like. Here the SimpleBookCatalog is the DbContext from before.
//This is the default strategy. It creates the DB only if it doesn't exist
Database.SetInitializer(new CreateDatabaseOnlyIfNotExists<SimpleBookCatalog>());
//Recreates the DB if the model changes but doesn't insert seed data.
Database.SetInitializer(new RecreateDatabaseIfModelChanges<SimpleBookCatalog>());
//Strategy for always recreating the DB every time the app is run.
Database.SetInitializer(new AlwaysRecreateDatabase<SimpleBookCatalog>());
Connection Strings for the SQL 4 CE provider are simpler (like, they are possible to memorize, which is amazing, considering how hard they are now). For example:
<add name="SimpleBookCatalog" connectionString="Data Source=C:\FooFoo\MyBooks.sdf" providerName="System.Data.SqlServerCe.4.0"/>
Here's some examples of fluent mappings and setting up relationships to give you an idea of the kinds of things you can do, while avoiding looking at any visual designers. It all depends on how clean you need your POCO (Plain Old CLR Objects) to be.
modelBuilder.Entity<Book>().HasKey(b => b.ISBN);
modelBuilder.Entity<Book>().HasRequired(b => b.Title);
modelBuilder.Entity<Book>().HasRequired(b => b.Author).WithMany(a => a.Books);
modelBuilder.Entity<Publisher>().Property(p => p.Name).MaxLength = 50;
modelBuilder.Entity<Author>().HasMany(a => a.Books).WithRequired();
These configurations can be batched up into a class that handles configuration for a specific entity so you can reuse them and more easily config like this.
builder.Configurations.Add(new BookConfiguration());
All this is a really basic example as a means of introduction and for my own learning, but so far I like it. It takes just a few minutes to get a lot done without much code. Since this is all Entity Framework, I can put an OData service on top of my model really quickly and then start consuming those services from iPhones or whatever.
It'll be interesting to take a sample like Nerd Dinner or MVC Music Store and change them to use Code First EF and the Razor View Engine and see if they are more readable and how many fewer lines of code they are.
Related Links
- Conventions for Code First
- Productivity Improvements for the Entity Framework
- Data Annotations in the Entity Framework and Code First
- Code First Walkthrough plus Sample Code
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
1) How do you manage important physical properties of the database like indexes? From what I see these would have to be managed outside the scope of the code-first specification. Yes I know some people will say that physical database design should be left up to the DBAs, but I think its not so clear cut these days. In fact I believe that sensible indexes can and should be designed a priori. I guess different persistence platforms may need different configurations physical designs as well.
2) This all starts to get into the area of "modelling" as in what they are trying to solve with SQL Server Modelling. If you define your model with c# as opposed to something higher level or more generic (e.g. "m") then are you restricting how easily you can repurpose that model? I can see this code-first+data annotations being an awesome end-to-end solutions to get something build quick and keep you brain focused on business value not keeping all the layers synchronized. But in some ways as soon as you want to step outside this scenario you will wish you had a model (or a variant of it ) that can be more easily reused. Perhaps even outside my app? I guess it is not very lean of me to consider such complications when this stuff can probably solve 90% of my requirements! So... is this all a precursor to SQL Server Modelling? I'm not sure but I suspect we are seeing a specialization of what they are trying to generalise with "m".
/braindump
There are still some rough patches in EF 4, for me the biggest being simple support for System.Enum code types, but it is really promising.
Jack's comment around physical properties of a DB is an interesting one, but presumably this is either a) implementation-specific and as such not something you'd necessarily concern yourself with when merely creating as data store with entity markup, or b) something that is actually supported by the Magic Unicorn feature and we've just not seen it yet.
Very interesting stuff and something I look forward to playing around with shortly.
"In CTP4 we have implemented the following conventions:
o Primary Key
o Relationship Inverse
o Foreign Key
o Pluralization of Table Names
o Support for Data Annotations
We posted details about all the Data Annotations we plan to support here The annotations supported in CTP4 are:
o Key
o StringLength
o ConcurrencyCheck
o Required
o Timestamp
o DataMember
o RelatedTo
o MaxLength
o StoreGenerated"...
EF CTP4 Released @ ADO.NET blog
(hmm - looks like Google's OpenId doesn't display your name very well here..)
Robin Osborne
Now please help them get IoC built into ASP.Net MVC (it's a pit of success thing) and work out a solution for managing interacting and overlapping models in our applications.
If this results in the option to do MEF-based CQRS, that would be "tha bomb", but hey, I'll settle for less to start with :-)
It's been a long time coming but I really like the new injection of the good, wholesome clean technologies with a code-first (as opposed to designer first) focus.
Razor and code-first EF looks like wins.
- Demis
Some comments from the Microsoft Magic Unicorn team:
1. Re: where is the injection point to specify physical database artifacts like indexes
>> This is something you can do with CTP4. We'll be posting some follow-up blog posts on the ADO.NET team blog in the next week about how to customize database initialization, seed it with sample data, or do all kinds of extra goodies.
2. Re: CodeFirst's relationship with SSMoS and model re-usability
>> This is coming, but it's going to take a bit longer. We're working on defining our experience of using Code First with various kinds of tooling/designers as optional workflows. So if you don't want to go 100% code, you'll have some alternatives you can augment your app with while still being primarily code focused.
Oh, and EF + Enums. Yeah, we know. It's coming :)
Jeff
"Razor" Page Syntax - A way to make pages/ViewEngine called "Razor." Your sites can be later be expanded to use all of ASP.NET MVC. It's a simple syntax that is easy to learn.
When I first read this it looked like you were suggesting Razor is not a full-fledged view engine, as capable as the current view engine. I had to comb through ScottGu's article until the VERY end, where he stated in the summary that:
<blockquote cite=ScottGu">You can also drop standalone .cshtml/.vbhtml files into your application and run them as single-pages – which also enables you to take advantage of it within ASP.NET Web Forms applications as well.</blockquote>
This asp/php style of programming available through Razor ought to be highlighted a bit more.
The whole disposable aspect of the entity framework is its greatest downfall. It seems like a small thing, but I don't think entity frameworks should create disposable. I realize the actual database classes (such as SqlConnection) are disposable, and I think okay. But frameworks designed to exist up the stack shouldn't require anything that ties your hands such as IDisposable.
The ASP.NET MVC examples use an instance of the entity framework without disposing (such as the music store example).
I noticed that you mentioned that it "sucked less". EF4 doesn't suck that bad. It can be made to work. I have a basic ActiveRecord pattern being generated by T4 templates sitting on top of a very simple repository. This creates workable code that doesn't suck to work with, but it could be so much better.
The entity framework has a few fundamental problems that hopefully can be solved in future releases.
1. Disposing should not be in this framework.
2. No particular pattern. EF4 tried to be the lowest common denominator, instead of picking a particular pattern for data access it leaves that up to the user. While that is the most flexible, I believe constraints are more liberating. If EF4 forced people down a simple pattern it would be more useful and suck less.
3. Working on multiple results set at a single time can create strange exceptions. If you follow a rigid pattern when using EF4 this can be avoided, but otherwise...
hope ms can finalize this ASAP, im a hardcore, linq to sql fan,
dont like the current EF, tried (DONT LIKE IT) still love linetoSQL, altho its does a horrible job with (inner joins and sub query), my solution, well just use SP for complex queries.
but this looks very promising, as long as its
EASIER, & BETTER!
im willing...
just that i cant work with CTP, what if the final version has revision, dont have the time to re-code for LIVE system.
loooking forward to final version.. what is that????
thanks
public string ISBN { get; set; }
but seems to change later to be an int, so when I added the details method, it gives a runtime error:
public ActionResult Details(int id)
I've been around quite a number of years, and i've *never* come across a project where we'd write stuff so we could 'repurpose' what we'd already got.
However, I'd love to see non theoretical, non trivial samples too. Just in case I'm wrong. Which I probably am.
Good work Scott. Keep it up.
Cheers,
Rob
You reference a map to ProductContext; What is ProductContext? Where is it created? Is it an ObjectContext or a DbContext? What kind of parameter are we needing so that it can accept a specified database? Etc. Etc. It'd be really nice if some of these kinds of things could be clarified.
Thanks for the information. I'm looking forward to see how Code'First' develops. I think that's a misnomer, though. I haven't spoken to a programmer yet that cared about making their database with code, just mapping to it.
I want the database to be scripted from the model really, and code is a great way to start building the model via conventions. IMHO, T-SQL is a horrible language for database and table creation and modification. M looks to be a huge improvement and when that gets tied to EDMX, I think we will have a great way to 1) provide standard classes of functionality that can evolve (I will have many tables that always are tied to a tenant id, and always have standard columns, others that do not, but fall into a different pattern (say M2M relationships), etc.) If I can model that at the conceptual level, then I can evolve my database at the appropriate level of abstraction. Ultimately, the model should be the key, again, IMO, because it is the thing that should know about both the store and the code, and the model should be rich enough to encompass all patterns, but simple enough to make the common things easy and the unusual possible. End soap box.
Random Google Open ID Guy - Yes, that was my mistake. ISBN should be an int. I changed it above.
Rob - Totally get you. You can pass in a regular Connection String to a DbContext derived class and, if you like, use CodeFirst (perhaps manually, perhaps via code generation) to describe an existing database.
If you already have an existing database why would you want to represent it in code? There are already great tools that will generate the mapping EDMX file from a database and let you visually tweak it if needed.
[)amien
I'm down with Magic Unicorns.
If I am using something like StuctureMap or Unity with MVC, I wonder what the best way would be to inject SimpleBookCatalog into the controller? Would it be to simply just extract an interface from it and inject ISimpleBookCatalog into the controller's constructor, or should I do something like nHibernate where you configure the session on the application start up?
wrt - "the dbcontext invade my top tiers? Will I still be able to cleanly separate the layers?"
As presently defaulted it is certainly possible for the dbcontext to infiltrate, but you can with work, knowledge and vigilance, cleanly separate the layers. I would not classify that as a pit of success default though.
It seems to me to be defaulting, at present, to getting something up and running for a very quick demo rather than defaulting to something that is truly architecturally sound. There has been a lot of progress on the different code generation templates, and a lot to like in the flexibility, but it is hard to get your head around what features require what when you start writing custom templates, at least in my own limited experience.
4. Code first programming model support for Compact 4.0 in ADO.NET Entity Framework: The Microsoft ADO.NET Entity Framework Feature Community Technology Preview 3 is an early preview of the code-first programming model for the ADO.NET Entity Framework 4.0. The code-first feature in the ADO.NET Entity Framework CTP3 release does not work properly with Compact 4.0. Future releases of code-first programming model for ADO.NET Entity Framework will provide support for code-first programming model for Compact 4.0.
SQL Compact Blog
public class DataContext : ObjectContext
And then in my constructor I am passing a named connection string to the base like so:
public DataContext() : base("name=TestDB", "TestDB") {}
My connection string is in the web.config and loks like this:
<add name="TestDB" connectionString="Data Source=(local);Initial Catalog=Test.Database;Persist Security Info=True;User Id=Test;Password=test" providerName="System.Data.SqlClient" />
However when I try to run this I get the following exception:
"The specified named connection is either not found in the configuration, not intended to be used with the EntityClient provider, or not valid."
Anybody have any thoughts?
Thanks, --Jim
Which namespace is the [RelatedTo] attribute in? There's one in System.ComponentModel.DataAnnotations but it doesn't have a RelatedProperty property, just ForeignKey and Property.
[)amien
Its great to see that this is something that will become part of the core.
Gavin.
public class DefinedType {
public int Id{get;set;}
public virtual string Typename{get;set;}
public virtual ICollection<DefinedObject> DefinedObjects {get;set;}
}
public class DefinedObject {
public int Id{get;set;}
public virtual DefinedType DefinedType{get;set;}
}
Now with the configuration on DefinedObject with HasRequired(t => t.DefinedType).WithMany(o => o.DefinedObjects) the EF tries to query DefinedType_Typename on DefinedObject why??
Is this a bug or is there a version which we don't know about?
Thanks,
PG
-Morder
I have some trouble with modifying a 1-1 relationship. I use a one direction relationship: Product -> Brand. When I modify the brand by selecting another existing one, the DbContext.SaveChanges() method creates a new Brand with the same values of the exsiting Brand but with a new Id. How do I solve this problem?
My model classes look like this:
[ModelBinder(typeof(ProductModelBinder))]
public class Product
{
[Key]
public int ProductId { get; set; }
public string ProductName { get; set; }
[ScaffoldColumn(false)]
public int BrandId { get; set; }
[DropDownList("Brands", "BrandId", "BrandName", "[Select brand]")] // See http://bit.ly/5Pv8eR
[RelatedTo(ForeignKey = "BrandId")]
public Brand Brand { get; set; }
}
}
public class Brand
{
[Key]
[RelatedTo(Property = "BrandId")]
public int BrandId { get; set; }
public string BrandName { get; set; }
}
I created a MVC2 View with EditorForModel() using the Product as model, which resulted in a textbox (ProductName property) and a DropDown listbox (Brand property). When I change the value of the dropdownlist, I use the ProductModelBinder.GetPropertyValue() method to retrieve the selected Brand from the BrandRepository using the Brand.BrandId value I received from the view. In the controller, I created the following Action:
[HttpPost]
public ActionResult Edit(int id, FormCollection productData)
{
Product entry = productRepository.GetByID(id);
try
{
if (ViewData.ModelState.IsValid)
{
// This works nice: Product.Brand is changed with the selected other Brand; Product.BrandId
// contains the old BrandId
UpdateModel(entry, productData.ToValueProvider());
// This works nice too, now Product.BrandId contains the BrandId of the selected Brand.
// The Attach() method of this repository executes DbSet<Product>.Attach() and
// DbContext.ObjectContext.ObjectStateManager.ChangeEntityState(product, EntityState.Modified)
productRepository.Attach(entry);
// SaveChanges() goes wrong: now Product.Brand contains a NEW Brand with the
// values of the selected one and with a new BrandId
productRepository.SaveChanges();
return RedirectToAction("Index");
}
ViewData["Brands"] = brandRepository.GetAll();
return View(entry);
}
catch (Exception ex) { return View(entry); }
}
Please help! What's going wrong?
would the Code First DbContext work as a WCF Data Service?
I tried it, but got he following error :
The server encountered an error processing the request. The exception message is 'On data context type 'CustomerService', there is a top IQueryable property 'Customer' whose element type is not an entity type. Make sure that the IQueryable property is of entity type or specify the IgnoreProperties attribute on the data context type to ignore this property.'. See server logs for more details.
>I tried it, but got he following error :
In CTP4, DbContext doesn't directly support exposing to DataService but here's a simple workaround:
http://romiller.com/2010/07/19/ef-ctp4-tips-tricks-wcf-data-service-on-dbcontext/
Below is a link to Tutorial for using Entity Framework with SQL Ce 4.0 as there is no design time support for SQL CE in VS2010
http://getsrirams.blogspot.com/2010/10/adonet-entity-data-model-for-sqlserver.html
Identity column issue in EF 3.5 with SQL CE 3.5 is fixed for SQL CE 4.0 in EF 4.
ScottGu also had a blog entry on Code-First and modifying the underlying database mappings. In both of these posts, you two show how you can: 1) map a "complex property" into columns within the entity database table and 2) in your post specifically, how to use inheritance. What I'm wondering, though, is is it possible to split a POCO class's attributes into multiple database tables?
For example, in the ASP.NET membership system, there is no way to associate a user's first and last name with the Member object without doing one of two things: 1) Using the standard ASP.NET Profile provider or 2) creating your own MembershipProvider. What I would like to do is have a "super POCO object" that includes all information about a user (particularly first and last name, office number/location...it's not really "profile data", it's a part of who a member is in my project) and have those attributes be stored in two or more tables. This prevents, for instance, having to access these "properties" of member via Member.Profile.FirstName, or Member.User.UserName. Conceptually, it's easier if I could just write Membe.FirstName or Member.UserName, etc.
Great stuff - but I found one issue that you may already be aware of...
According to the EF Data Annotations setting the Required Attribute on a navigation reference property (ie a 1 to many relationship instead of a 0..1 to many).
I tried this in my model like so:
[Required]
public virtual Person Person { get; set; }
but its generating the PersonID column as nullable and its not giving an exception when leaving this property null. However, I tried setting the HasRequired method in the model Builder and it DID work as expected.
modelBuilder.Entity<Inquiry>().HasRequired(b => b.Person);
So I'm guessing this might just be a feature that is not yet wired up in the beta. Or please feel free to let me know if I am just missing something.
Thanks again for the awesome work you guys are doing.
Comments are closed.
Thanks