Entity Framework 4.1 DbContext Data Access
Julie Lerman
1. DbContext
- .Add
- .Attach is not an update but tells EF to track the entity.
- .Remove
- .Entry.State=Modified Added Deleted
var newPerson = context.People.Add(new Person { Name = "Julie"});
Assert.IsTrue(context.Entry(newPerson).State==System.Data.EnitityState.Added);
//context.Aliases.Attach(existingAlias); //line below willl do implicit
context.Entry(existingAlias).State = System.Data.EntityState.Modified;
Assert.IsTrue(context.Entry(exisitingAlias).State == System.Data.EntityState.Modified);
var query = from p in context.Person where p.Id == 3 select p;
var person = query.FirstOrDefault();
//use instead ->
var person = context.People.Find(3); //uses context cach as a bonus
//objects managed by the cache
ObservableCollection<Person> = context.People.Local
Proxies, Change Tracking & Lazy Loading
[TestMethod]
public void ObjectsGetDynamicProxies()
{
var context = new TwitterContext();
var alias = context.Aliases.FirstOrDefault();
Assert.IsTrue(alias.GetType().FullName.StartsWith("System.Data.Entity.DynamicProxies"));
}
[TestMethod]
public void ObjectsAreInstantlyChangeTracked()
{
var context = new TwitterContext();
var alias = context.Aliases.FirstOrDefault();
alias.Name = "abcde";
//context.ChangeTracker.DetectChanges(); when not using dynamic proxy
Assert.IsTrue(((context as IObjectContextAdapter).ObjectContext).ObjectStateManager.GetObjectStateEntries(EntityState.Modified).Count() == 1);
}
public class Alias
{
public virtual int AuthorKey { get; set; } //will create lazy loading dynamic proxy
public virtual ICollection<Tweet> Tweets { get; set; } //one marked as virtual will enable lazy loading
public string Email { get; set; } //must be virual if you want change tracking
public string AliasPlusName //no setter so will be skipped
{
get { return Name + "(" + UserName + ")"; }
}
}
Change Tracker
- DetectChanges called by:
- DbSet .Find .Local .Remove .Add .Attach
- DbContext .SaveChanges .GetValidationErrors .Entry
- DbChangeTracker.Entries
//if you do NOT want DetectChanges to get called
context.Configuration.AutoDetectChangesEnabled = false;
Get To ObjectContext
- var oContext = (myDbContext as ObjectContextAdapter).ObjectContext;
- Linq to Entities CompiledQuery: No DbContext, No Adapter.ObjectContext
- EF5 : http://blogs.msdn.com/b/stuartleeks/archive/2012/06/12/entity-framework-5-controlling-automatic-query-compilation.aspx
[TestMethod]
public void CanGetDatabaseValuesFromEntry()
{
var context = new TwitterContext();
var alias = context.Aliases.FirstOrDefault();
DbPropertyValues dbValues = context.Entry(alias).GetDatabaseValues();
Assert.IsNotNull(dbValues);
}
//when savechanges and an optimistic concurrency error was thrown
var entry = context.Entry(alias);
entry.OriginalValues.SetValues(entry.GetDatabaseValues()); //client win
entry.Reload(); //or use for server win
From existing database
- edmx file -> DbContext T4 Template -> Simple POCO Classes and DbContext
- ADO.Net DbContext Generation
2. Code First Database Initialization
- Connectionstring Available (else use buildstring) -> Database Exists (else create database) -> Use Database
- Buildstring: SQL Server Express using Context Strong Name
- eg. namespace DAL class myContext -> DAL.myContext will be the database name
public class MyContext : DbContext
{
public MyContext() : base("MyOwnDatabaseName") {}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<Privacy>();
modelBuilder.Configurations.Add(new AliasConfiguration());
}
}
- Read classes & configurations
- Build in-memory Model & mappings
- Compare model to EdmMetadata table
- Changes? Follow init strategy (default: throw exception)
//on application_start()
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyContext>());
IDatabaseInitializer
- CreateDataseIfNotExists
- DropCreateDatabaseIfModelChanges
- DropCreateDatabaseAlways
- YourCustomDBInitialize
Code First Migrations
public class MyInitializer : DropCreateDatabaseIfModelChanges<MyContext>
{
protected override void Seed(TwitterContext context)
{
new List<Alias> { ... }.ForEach(b => context.Aliases.Add(b));
base.Seed(context); //calls SaveChanges, so you don't have to
}
}
Database.SetInitializer(new MyInitializer());
Specify the Database (new or existing)
<connectionStrings>
<add name="TwitterContext"
connectionString="Data Source=.;Initial Catalog=TweetTweet;Integrated Security=True"
providerName="System.Data.SqlCient" />
</connectionStrings>
Make sure that initializer is on default CreateDataseIfNotExists, cause it will think you modified the database since EdmMetadata is missing!
- DbModelBuilder.Conventions.Remove(IncludeMetadataConvention);
- Database.SetInitialize<MyContext>(null);
- <appSettings><add key="DatabaseInitializerForType DAL.MyContext, DataAccessAssembly" value="Disabled" /></appSettings>
Generic Types in XML configuration
- IList<T> will be IList`1 and IList[]
- IDictionary<K,V> will be IDictionary`2 and IDictionary[,]
System.Data.Entity.Database
- Create, CreateIfNotExists, Delete, Exists
- GetHashCode
- ExecuteSqlCommand, SqlQuery
- CompatibleWithModel
- Initialize, SetInitializer(TContext)
- Connection, DefaultConnectionFactory
3. More on Entity Framework