Entity Framework 4.1 Change Tracker EF状态跟踪函数解析

Skip
to main content

Data
Developer Center

Sign
in

United
States (English)

HomeLibraryLearnDownloadsSupportCommunityForums

Change Tracker API

Entity Framework 4.1 Change Tracker

Julie Lerman

http://thedatafarm.com

Published: June 2011

Download the code for this article



The Entity Framework 4.1 DbContext keeps track of the state of objects it‘s managing and provides a number of ways for you to access that state information whether your entities come from the Code First, Model First or Database First workflow. DbContext also
gives you access to features like explicitly loading related data.

In this article, I’ll be using two simple classes, Blog and Post, that are managed by the BlogContext class which inherits from DbContext.

public class Blog 
    { 
        public Blog() { 
            Posts = new List<Post>(); 
        } 
        public int Id { get; set; } 
        public string Title { get; set; } 
        public string BloggerName { get; set; } 
 
        public virtual ICollection<Post> Posts { get; set; } 
    } 
 
    public class Post 
    { 
        public int Id { get; set; } 
        public string Title { get; set; } 
        public DateTime DateCreated { get; set; } 
        public string Content { get; set; } 
        public int BlogId { get; set; } 
 
        public Blog Blog { get; set; } 
    } 
 
   public class BlogContext : DbContext 
   { 
        public DbSet<Blog> Blogs { get; set; } 
        public DbSet<Post> Posts { get; set; } 
   }

Accessing State Information of a Single Object

The Blog and Post class are only aware of the current values of their properties. They know nothing about their state or their history. The DbContext class keeps track of the state information in a DbEntityEntry object for each object it’s managing.

DbContext.Entry( ) is the entry point to the state information for a particular object instance. Just pass in an object to return its corresponding DbEntityEntry. If the object is not yet being tracked, the context will begin tracking it as a result of this
call.

Here is some code that instantiates a new BlogContext, and uses that to retrieve a single blog.

var db = new BlogContext(); 
   var post = db.Posts.First();

With the post in hand, you can grab it’s entry:

var stateInfo = db.Entry(post);

Inspecting the stateInfo in the debugger, you can see that it exposes not only a State (which is Unchanged) but CurrentValues, OriginalValues and a pointer back to the instance of the Post it represents.

Figure 1

I’ll modify a property of the Post.

post.Title = "What‘s happening at " + DateTime.Now.ToShortTimeString();

Inspecting the stateInfo variable will reveal that it has not changed. The reason is that the DbContext needs to take another look at post before it will be aware of the changes. You can do that with:

db.ChangeTracker.DetectChanges();

The stateInfo.State value will now be Modified as you can see in Figure 2.

Figure 1

Inspecting what’s in the CurrentValues and OriginalValues isn’t quite as obvious. The DbPropertyValues that they both inherit from is a read-only hash set of property names and values. It exposes the names through its PropertyNames property and then you can
use those to get to the actual values.

Here’s a simple method which will take any DbPropertyValues object, iterate through its PropertyNames, and then display that property name along with its associated.

private static void IterateValues(DbPropertyValues propertyValues) 
        { 
            foreach (var propertyName in propertyValues.PropertyNames) 
            { 
                Console.WriteLine("   {0} = {1}", 
                                  propertyName, propertyValues[propertyName]); 
            } 
        }

Passing the stateInfo.OriginalValues into this method results in a list of the original values of the Post object.

Id = 1 
   Title = ForeignKeyAttribute Annotation 
   DateCreated = 3/15/2011 12:00:00 AM 
   Content = Mark navigation property with ForeignKey 
   BlogId = 1

The CurrentValues property results in:

Id = 1 
   Title = What‘s happening at 2:06 PM 
   DateCreated = 3/15/2011 12:00:00 AM 
   Content = Mark navigation property with ForeignKey 
   BlogId = 1

Notice that the Title now reflects the change that was made in the code above.

Retrieving the Current Database Values for an Object

In addition to the OriginalValues and CurrentValues property, Entry exposes a method called GetDatabaseValues. This triggers a query of the database to retrieve the current database values for that object and returns them as a DbPropertyValues type. You can
pass that type to the IterateValues method to inspect it.

IterateValues(stateInfo.GetDatabaseValues);

The method outputs the following data:

Id = 1 
   Title = ForeignKeyAttribute Annotation 
   DateCreated = 3/15/2011 12:00:00 AM 
   Content = Mark navigation property with ForeignKey 
   BlogId = 1

In this case, nobody has modified the post since it was retrieved from the database, so the database values match the original values of the post instance.

Working with Multiple State Entries

DbContext.Entry() allows you to get the state information for a single, known object. But what if you want to look at all of the entries that the context is tracking?

For example, here‘s some code that retrieves all blogs then makes edits— modifying the title of one blog, deleting another and then I adding a new one.

var blogs = db.Blogs.Include("Posts").ToList(); 
            blogs[0].Title = "Rebranding the Blog"; 
            db.Blogs.Remove(blogs[1]); 
            var newBlog = new Blog { BloggerName = "Liz", Title = "The Marrying Kind" }; 
            db.Blogs.Add(newBlog);

In order to look at the universe of DbEntityEntry types that the context is managing, you need to leverage the DbContext.ChangeTracker property. It has a method to return all of the entries, Entries().

Here is some code that iterates through all of the entries and writes out the title and state of each entry. Recall that in Figure 1, you saw that DbEntityEntry provides a pointer back to the object that it represents, the Entity property.

foreach (var entry in db.ChangeTracker.Entries<Blog>()) 
         { 
             Console.WriteLine("Blog: Title {0}, State: {1}", 
                                ((Blog)entry.Entity).Title, entry.State); 
         }

This iteration outputs the following, including a blog that was left untouched during the edits.

  • Blog: Title The Marrying Kind, State: Added
  • Blog: Title Rebranding the Blog, State: Modified
  • Blog: Title My Life as a Blog, State: Deleted
  • Blog: Title Tweeting for Dogs, State: Unchanged

Entries optionally uses generics to let you specify a type to retrieve. If you have other objects besides Blog being tracked but you are only interested in Blog types, you can specify that.

A handy result of this is that now EF is quite sure that all of the entries returned are for Blog types and you’ll find that Entity becomes strongly typed. You can change the above code to

foreach (var entry in db.ChangeTracker.Entries<Blog>()) 
   { 
       Console.WriteLine("Blog: Title {0}, State: {1}", 
                          entry.Entity.Title, entry.State); 
   }

Because Entries is enumerable you can also perform LINQ to Objects queries.  Here, the query filters out all of the Unchanged Blog entities.

foreach (var entry in db.ChangeTracker.Entries<Blog>() 
                          .Where(e => e.State != EntityState.Unchanged))

Explicitly Load Related Data with Entry.Collection

In addition to the state information, there are also methods to access information about the properties of an entry’s entity.  Figure 2 shows all of the properties and methods of the DbEntityEntry class.

Figure 3

With an object instance, you can use strong typing to navigate to related data.

var blog = db.Blogs.First(); 
            var posts = blog.Posts();

But if you didn’t have access to the object instance you can use the DbEntityEntry.Collection method to write generic code to access a collection property.

Continuing the example that retrieves all of the Blog entries then iterates through them, you could use the Collection method to access the Posts of each blog. Collection.CurrentValue returns a generic list of the objects, so you need to do a little digging
to get at the actual posts. Here I’ve cast the CurrentValue to a List<Post> from which I can get a count.

foreach (var entry in db.ChangeTracker.Entries<Blog>()) 
   { 
       var posts = entry.Collection(b=>b.Posts); 
       Console.WriteLine("{0}", ((List<Post>)posts.CurrentValue).Count) 
   }

If the entries are not strongly typed, removing the <Blog> typing, you replace the lambda expression that represents the property with a string.

foreach (var entry in db.ChangeTracker.Entries()) 
   { 
       var posts = entry.Collection("Posts"); 
       Console.WriteLine("{0}", ((List<Post>)posts.CurrentValue).Count) 
   }

Not only can you access the collection, but through this ChangeTracker, you can also load the collection from the database if you are not relying on lazy loading. You might want to check that it hasn’t been loaded before doing that so you won’t waste the trip
to the database. If IsLoaded is false, then Entity Framework will execute a query on the fly and grab the posts for the particular blog from the database.

Note that Collection.Load will fail with entities that are Deleted or Added. Therefore they are both filtered out in this example.

foreach (var entry in db.ChangeTracker.Entries<Blog>() 
    .Where(e=>e.State != EntityState.Deleted && e.State != EntityState.Added)) 
{ 
    Console.WriteLine(entry.Entity.Title); 
    var posts = entry.Collection b=>b.Posts); 
    if (posts.IsLoaded == false) 
    { 
      posts.Load(); 
    } 
    Console.WriteLine("# Posts: {0}", ((List<Post>)posts.CurrentValue).Count); 
}

Filtering and Sorting Explicitly Loaded Related Data

Explicit loading has been available in Entity Framework since .NET 3.5. However, the Load features in Entity Framework 3.5 and 4.0 does not provide any support for fine-tuning what is loaded. It is an all or nothing gambit — you can only load the complete collection.

Loading through the ChangeTracker, however, does provide you with the ability to query when loading. Collection has a method called Query which returns the query that Entity Framework is going to use to get that related data from the database. You can modify
that query with LINQ.

Here is an example of asking the query to only bring back Posts which have the word “Annotation” in their title.

posts.Query().Where(p => p.Title.Contains("Annotation")).Load();

Note that if you use the string parameter to specify the collection, the strong-typing that LINQ depends on for this will not work.

Notice what’s available from Intellisense when using the string parameter as shown in Figure 4.

Figure 4

You can fix this by strongly typing the collection. Notice that in Figure 5, LINQ methods are now available from the Query method.

Figure 5

Summary

This article has demonstrated a substantial set of the features that you can take advantage of in the Entity Framework 4.1 Change Tracker API. Along the way, you’ve also learned some tricks you’ll need to know as you use the features in more advanced scenarios.

? 2015 Microsoft. All rights reserved. Terms
of Use
 |  Trademarks |  Privacy
Statement

  • Site
    Feedback
  • 时间: 2024-08-03 10:10:34

    Entity Framework 4.1 Change Tracker EF状态跟踪函数解析的相关文章

    Entity Framework Code First - Change Tracking

    In this post we will be discussing about change tracking feature of Entity Framework Code First. Change tracking allows Entity framework to keep track of all the changes in entities' data. It might involve adding new entities to entities collection o

    Entity Framework 5.0系列之EF概览

    概述 在开发面向数据的软件时我们常常为了解决业务问题实体.关系和逻辑构建模型而费尽心机,ORM的产生为我们提供了一种优雅的解决方案.ADO.NET Entity Framework是.NET开发中一种由ADO.NET驱动的ORM框架,使用Entity Framework开发人员可以不必考虑数据的基础数据表和列,在处理数据时能够以更高的抽象级别进行工作,并能够以相对传统开发编写更少的代码来创建和维护应用程序. 我们知道面向对象的编程与数据存储系统的交换提出了一个难题:类结构通常同关系数据表组织结构

    Entity Framework(实体框架 EF)

    什么是Entity Framework呢(下面简称EF)? EF(实体框架)是ADO.NET中的一组支持开发面向数据的软件应用程序的技术,是微软的一个ORM框架.ORM(对象关系映射框架):指的是面向对象的对象模型和关系型数据库的数据结构之间的相互转换. 如此官方的解释,你懂吗? 我理解的是,业务实体在内存中表现为对象,在数据库中表现为数据,内存中的对象之间,存在关联和继承关系,而在数据库中,关系数据无法直接表达这些关系.而对象-关系映射(ORM)就是解决这一问题的.ORM作为一个中间件,实现程

    重新认识了下Entity Framework

    什么是Entity Framework Entity Framework是一个对象关系映射O/RM框架. Entity Framework让开发者可以像操作领域对象(domain-specific objects)那样操作关系型数据(relational data). Entity Framework减少了大部分通常需要编写的数据操作代码. Entity Framework中可以使用LINQ来查询数据,使用强类型(strongly typed objects)来检索和操作数据. Entity F

    重新认识Entity Framework

    什么是Entity Framework Entity Framework是一个对象关系映射O/RM框架. Entity Framework让开发者可以像操作领域对象(domain-specific objects)那样操作关系型数据(relational data). Entity Framework减少了大部分通常需要编写的数据操作代码. Entity Framework中可以使用LINQ来查询数据,使用强类型(strongly typed objects)来检索和操作数据. Entity F

    Entity Framework 学习

    Entity Framework 学习初级篇1--EF基本概况... 2 Entity Framework 学习初级篇2--ObjectContext.ObjectQuery.ObjectStateEntry.ObjectStateManager类的介绍... 7 Entity Framework 学习初级篇3-- LINQ TOEntities. 10 Entity Framework 学习初级篇4--EntitySQL. 17 Entity Framework 学习初级篇5--ObjectQ

    Entity Framework 6 Recipes 2nd Edition(9-4)译-&gt;Web API 的客户端实现修改跟踪

    9-4. Web API 的客户端实现修改跟踪 问题 我们想通过客户端更新实体类,调用基于REST的Web API 服务实现把一个对象图的插入.删除和修改等数据库操作.此外, 我们想通过EF6的Code First方式实现对数据的访问. 本例,我们模拟一个N层场景,用单独的控制台应用程序作为客户端,调用Web API服务(web api项目). 注:每个层用一个单独的解决方案,这样有助于调试和模拟N层应用. 解决方案 假设我们一个如Figure 9-4.所示模型 Figure 9-4. A 客户

    [转]Entity Framework技术导游系列开篇与热身

    学习Entity Framework技术期间查阅的优秀文章,出于以后方便查阅的缘故,转载至Blog,可查阅原文:http://blog.csdn.net/bitfan/article/details/12779517 在微软平台写程序有年头了,随着微软数据存取技术的持续演化,我在程序中先后使用过ODBC.DAO.ADO.ADO.NET.LINQ to SQL. Entity Framework这些技术. 近几年来,Entity Framework(后文简称EF)的地位日益上升,成为微软数据存取技

    Entity Framework技术导游系列开篇与热身

    在微软平台写程序有年头了,随着微软数据存取技术的持续演化,我在程序中先后使用过ODBC.DAO.ADO.ADO.NET.LINQ to SQL. Entity Framework这些技术. 近几年来,Entity Framework(后文简称EF)的地位日益上升,成为微软数据存取技术的主角,也是我个人在开发应用程序时首选的数据存取技术,同时我也在博客园等专业网站上看到业界许多项目也都使用了EF.我放在CSDN下载频道的几讲Entity Framework 5课件(http://download.