So I just recently turned my laptop into a 4.0 workstation since it is kind of expendable and I won’t feel inclined to nerd rage if it gets tooled. With this step forward, I decided that I probably won’t be doing much with the old Entity Framework version since the new one is supposed to be the end all/be all until the next end all/be all version comes out. What does this mean for you? This post could be filled with useful information or f— all. Just depends on what you care about.
After doing a little reading, and by little I mean as little as humanly possible while still having an idea of what I’m doing, I got the new EF to persist an object. Mind you, it took a bit of patience followed by slamming my head into my desk and then more patience, but I did it.
First off, the actual creating of the edmx file is the same, there’s really no difference. You still go through the wizard, you still go through the connection set up, and you still grab the tables you want. No change there.
However, once you have it created, click on the .edmx file in your solution explorer, right click, then click properties. You’ll see something like this:
You can see where there is nothing in the Custom Tool area of properties. That’s because I deleted the text. That’s the first part of what you need to do. Next is creating the needed classes. For this example I have two, Ad and Newspaper:
namespace Beta2Test.Data.Entity
{
public class Ad
{
public Int32 Id { get; set; }
public DateTime CreatedDate { get; set; }
public String Name { get; set; }
public DateTime? LastUpdated { get; set; }
public Int32 Height { get; set; }
public Int32 Width { get; set; }
//Note:
//If properties are to be lazy loaded, must be virtual
public virtual IList<Newspaper> Newspapers { get; set; }
}
}
and
namespace Beta2Test.Data.Entity
{
public class Newspaper
{
public Int32 Id { get; set; }
public Int32 Circulation { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime LastUpdated { get; set; }
public String Name { get; set; }
//Note:
//If properties are to be lazy loaded, must be virtual
public virtual IList<Ad> Ads { get; set; }
}
}
Three things you might notice:
- The classes don’t inherit from anything. Yay
- The namespace is Beta2Test.Data.Entity. Fact is, I left it there to show that the namespace can now be anything. Yay
- Both collection properties are virtual. This is a must when dealing with collections that represent a relationship and you have lazy loading enabled (See below context class). For this, there is a many to many relationship between Ads and Newpapers. The Ads and Newspapers collection represent that. Why do that have to be virtual? Has to do with Entity Framework needing a way to override the properties so it can tell when they are accessed. (Read: Lazy Loading)
- There is no fourth thing. You are wrong if you think there is.
So far so good, but what about that like whole linking to that persistence layer stuff. You know like them entity objects yo.
Thank you for that poorly worded inquiry, but valid none the less. Remember those entity context classes that it used to generate for you? Well that whole “custom tool” dohickey I removed would have built it for me. However, that’s not the path we’re going down anymore. It’s now time to brave the unknown. We, yes we, will build the context. Yes the context.
using System.Data.Objects;
using Beta2Test.Data.Entity;
namespace Beta2Test.Data
{
public class Beta2TestContext : ObjectContext
{
private ObjectSet<Ad> _ads;
private ObjectSet<Newspaper> _newspapers;
public Beta2TestContext() : base("name=InterviewDemoEntities", "InterviewDemoEntities")
{
_ads = CreateObjectSet<Ad>();
_newspapers = CreateObjectSet<Newspaper>();
//This makes sure the context lazy loads by default
ContextOptions.LazyLoadingEnabled = true;
}
///
/// This is used to set up the "queryable" collection.
///
public ObjectSet<Ad> Ads
{
get
{
return _ads;
}
}
public ObjectSet<Newspaper> Newspapers
{
get
{
return _newspapers;
}
}
///
/// This creates an ad that allows it "to be used with the Entity Framework."
///
///
public Ad CreateAttachedAd()
{
return EntityContext.Context.CreateObject<Ad>();
}
public Newspaper CreateAttachedNewspaper()
{
return EntityContext.Context.CreateObject<Newspaper>();
}
}
}
The CreateAttached methods aren’t needed on the Context class itself, I just put them there. They could go on the Ad/Newspaper class but at this point I’m not sure if that blurs the lines or not. Haven’t gotten into best practices mode yet.
You may notice the ObjectSet collections on the context. These are the collections you will query to get items most likely, much like the old context class from the last version:
Context.Ads.Where(ad => ad.Id == 1)
Pretty nice, huh?
Also, I noted above about the virtual properties on the Ad/Newspaper classes and how it had to do with lazy loading.
ContextOptions.LazyLoadingEnabled = true;
That line and the virtual properties ensure that lazy loading will occur when the property is used. Both have to be used together. Now you don’t have to put that line in the constructor if you do create a context every time you are retrieving things, you could just set it on a case by case basis. Up to you. I use a singleton like context so I just set it true in the constructor and don’t worry about it.
The main thing I had trouble with was this line:
public Beta2TestContext() : base("name=InterviewDemoEntities", "InterviewDemoEntities")
Because I wasn’t sure what the second string should be. First one is easy, it’s whatever the key in the config file for the connection string. Second, chances are, is the same depending on how you set it all up in the wizard. I think it’s the same name when you right click on the opened .edmx file and view properties. There should be a property named “Entity Container Name”.
Here’s a method I used for testing to create an Ad:
public Ad CreateAd(Boolean persist)
{
Ad ad = null;
if (persist)
{
ad = EntityContext.Context.CreateAttachedAd(); //This is the CreateAttachedAd method above in use
}
else
{
ad = new Ad();
}
ad.CreatedDate = DateTime.UtcNow;
ad.Height = RandomTool.RandomInt32(0, 10);
ad.LastUpdated = DateTime.UtcNow;
ad.Name = RandomTool.RandomString(10);
ad.Width = RandomTool.RandomInt32(0, 10);
if (persist)
{
AddEntityForRemoval(ad); //Ignore, for persistence removal after testing is completed
EntityContext.Context.Ads.AddObject(ad); //Ad the ... ad to the context
EntityContext.Context.SaveChanges(); //Persist
}
return ad;
}
There are some things you can ignore because I didn’t feel like removing code. As you can see though, it’s not very complicated. Use the CreateAttachedAd, fill in the properties, ad to the Ad collection on the context, and save.
The End
Sure you had to do more work than in the earlier version of Entity Framework, but on the other hand I have a fully detached class that can inherit from any class I WANT it to as opposed to the EntityObject class. Not to mention I now have an easy way to do lazy loading. Far cleaner than the old way I did it. The other interesting thing to note, and this may not be a big deal to anyone else, but above I have a note about this:
AddEntityForRemoval(ad); //Ignore, for persistence removal after testing is completed
This method just adds the object to a collection of EntityObjects that later I use to delete the object from the database on test cleanup. After moving to this version of EF, I had to change it to a collection of Objects. Interesting thing is:
EntityContext.Context.DeleteObject(currentObject);
Didn’t care. It still knew that the object was attached to the context somehow, despite the CLASS not being an EntityObject. Just an odd note.
Two Errors I came across:
System.InvalidOperationException: Mapping and metadata information could not be found for EntityType ‘Beta2Test.Data.Ad’.
If you get this, there is a really good chance you screwed up a property name or are missing it completely. If the property name doesn’t exactly match one in the .edmx class designer template, you’re screwed. If you don’t have it on the class but it’s on the class designer template, you’re screwed. If you add it to the class designer template, it has to be on the class as far as I can tell. Now, it doesn’t have to be public. I have tested it as private and it works just fine.
The required property ‘Newspapers’ does not exist on the type ‘Beta2Test.Data.Ad’.
This most likely happens if you didn’t remember to make it a property:
public IList<Newpaper> Newpapers; //Forgot the { get; set; }
public IList<Newpaper> Newpapers { get; set; }; //Forgot the Virtual
Opps.