之前的两篇有关EF4.1的文章反响不错,感谢大家的支持!想体验EF4.1的新功能?RTW版本已经发布啦,http://www.microsoft.com/downloads/en/details.aspx?FamilyID=b41c728e-9b4f-4331-a1a8-537d16c6acdf&displaylang=en

Entity Framework 4.1 DbContext使用记之一——如何查找实体? DbSet.Find函数的使用与实现

Entity Framework 4.1 DbContext使用记之二——如何玩转本地实体? DbSet.Local属性的使用与实现

 

今天的主题是如何玩转EF4.1中实体的属性。实体的属性其实是我们使用EF来访问和修改实体的关键。在EF以前版本中,如果我们一般会直接访问对象的属性,比如得到PersonID大于100的所有Person实体的ID和Name:

using (var context = new MyContext())
{
    var people = context.People.Where(p => p.PersonID > 100);
    for (var p in people)
    {
        Console.WriteLine("ID: {0}, Name:{1}", p.PersonID, p.Name);
    }

}

但是如果要得到ObjectContext所track的实体属性的OriginalValues(原始值)和CurrentValues(当前值),则不是很方便。

而在EF4.1中,由于我们可以使用DbContext.Entry()或DbContext.Entry<T>()来得到DbEntityEntry或DbEntityEntry<T>对象。通过DbEntityEntry (DbEntityEntry<T>)的OriginalValues和CurrentValues属性,我们可以方便地得到相应的属性集合(DbPropertyValues)。通过DbEntityEntry (DbEntityEntry<T>)的Property(Property<T>)方法得到DbPropertyEntry(DbPropertyEntry<T>)之后,我们也能通过相应的OriginalValue和CurrentValue属性得到单个属性的原始值和当前值。感觉有点绕?看看下面的这些例子应该就简单明了多了!

using (var context = new MyDbContext())
{
    // 有关Find方法,请看EF4.1系列博文一
    var person = context.People.Find(1);

// 得到Person.Name属性的当前值
    string currentName = context.Entry(person).Property(p => p.Name).CurrentValue;

// 设置Person.Name属性的当前值
    context.Entry(person).Property(p => p.Name).CurrentValue = "Michael";

// 通过string值"Name"来获得DbPropertyEntry,而非通过Lambda Expression
    object currentName2 = context.Entry(person).Property("Name").CurrentValue;
}

再来看看如何得到实体的所有属性的OriginalValue(原始值),CurrentValue(当前值)和DatabaseValue(数据库值)吧:

using (var context = new MyDbContext())
{
    var person = context.People.Find(1);

// 改变对应的实体的Name属性
    person.Name = "Michael";

// 改变对应属性的数据库值
    context.Database.ExecuteSqlCommand("update Person set Name = 'Lingzhi' where PersonID = 1");

// 输出对应实体所有属性的当前值,原始值和数据库值
    Console.WriteLine("Current values:");
    PrintValues(context.Entry(person).CurrentValues);

Console.WriteLine("\nOriginal values:");
    PrintValues(context.Entry(person).OriginalValues);

Console.WriteLine("\nDatabase values:");
    PrintValues(context.Entry(person).GetDatabaseValues());
}

这里用到的PrintValues函数实现如下:

public void PrintValues(DbPropertyValues values)
{
    foreach (var propertyName in values.PropertyNames)
    {
        Console.WriteLine("Property {0} has value {1}",
                          propertyName, values[propertyName]);
    }
}

这里具体的输出大家就当小练习吧,呵呵。

下面再为大家介绍两个我个人认为比较cool的小功能:

1) 设置多层的复杂类型的属性。

例如,Person实体含有Address复杂类型属性(Complex - type),Address又含有City这一string类型属性,那么我们可以这样来获得City这一属性的当前值:

// 使用Lambda Expression
string city = context.Entry(person).Property(p => p.Address.City).CurrentValue;

// 使用string类型的属性表达
object city = context.Entry(person).Property("Address.City").CurrentValue;

这里层次的次数其实没有限制,可以一直点下去的。:)   EF的内部实现也是使用递归调用的方式,之后会有详细的介绍。

2) 克隆含有实体属性当期值,原始值或数据库值的对象。

我们可以使用DbPropertyValues.ToObject()方法来克隆将DbPropertyValues中相应的属性和值变成对象。

using (var context = new MyDbContext())
{
    var person = context.People.Find(1);
    var clonedPerson = context.Entry(person).GetDatabaseValues().ToObject();

}

这里克隆到的对象不是实体类,也不被DbContext所跟踪。但这样的对象在处理数据库并发等问题时会很有用。

又到了探寻以上介绍的功能的内部实现的时候啦!我们还是照例使用.NET Reflector来查看EntityFramework.dll。 大家可以打开Reflector和我一起来做个简单的分析。

首先是从DbContext.Entry方法得到DbEntityEntry。Entry方法通过调用DbEntityEntry internal的构造函数DbEntityEntry(InternalEntityEntry internalEntityEntry)来创建一个DbEntityEntry对象。这里的InternalEntityEntry又是通过DbContext.InternalContext和先前传入Entry函数的实体对象来生成的。

public InternalEntityEntry(InternalContext internalContext, object entity)
{
    this._internalContext = internalContext;
    this._entity = entity;
    
    // ObjectContextTypeCache应该是EF内部保存的静态的类型缓存,用于查找实体类型
    this._entityType = ObjectContextTypeCache.GetObjectType(this._entity.GetType());
    
    // InternalContext.GetStateEntry内部则调用了ObjectStateManager.TryGetObjectStateEntry方法
    this._stateEntry = this._internalContext.GetStateEntry(entity);
    if (this._stateEntry == null)
    {
        this._internalContext.Set(this._entityType).InternalSet.Initialize();
    }
}

下面来看看DbEntityEntry.CurrentValues/OriginalValues。CurrentValues和OriginalValues属性都是DbPropertyValues类型的。DbPropertyValues可以被理解为对于一个实体或复杂类型所有属性信息的集合。我们通过PropertyNames属性拿到其所有属性的名字,通过GetValue或SetValues方法得到或设置属性值。还能通过我们之前讨论的ToObject方法来克隆一个有用与对应实体或复杂类型对象一样属性值的对象。演示一下如何使用DbPropertyValues.SetValuesF方法:

using (var context = new MyDbContext())
{
    var person = context.People.Find(1);
    var person1 = new Person { PersonID = 1, Name = "Michael" };
    var person2 = new Person { PersonID = 1, Name = "Lingzhi" };

var entry = context.Entry(person);
    
    // 这里直接将拥有相应属性值的实体对象直接赋给SetValues方法,直接对person实体的CurrentValues和OriginalValues进行赋值
    entry.CurrentValues.SetValues(person1);
    entry.OriginalValues.SetValues(person2);
}

这里SetValues内部首先调用了DbHelpers.GetPropertyGetters方法。DbHelpers是System.Data.Entity.Internal命名空间下Internal的类,包含了许多静态的辅助方法。这里的GetPropertyGetters顾名思义就是得到属性Getter方法delegate的集合,内部当然是通过PropertyInfo以及.NET Reflection来完成。有了这个Getter方法delegate的集合,我们就能方便地拿到传入SetValues方法的对象的所有属性值了,最后则按照所得到的属性值来进行赋值。

接着我们再来看看如何从DbEntityEntry.Property得到DbPropertyEntry。我们可以传递两种property的表达方式给DbEntityEntry.Property方法:1) Lambda Expression   2) string类型的property表示。先来说说1),在将Lambda Expression解析为对应property时,EF使用了辅助静态方法DbHelpers.ParsePropertySelector,ParsePropertySelector又调用了另一静态辅助方法DbHelpers.TryParsePath。具体算法在这里就不做深入解析,简单说这个TryParsePath方法就是递归地将Lambda Expression所表示的property层层深入地获取到,例如像这样的Lambda Expression:person => person.Address.City最后就变为"Address.City"。在解析完毕之后,ParsePropertySelector其实也是将Lambda Expression变成了string类型的property表示。自然,EF此时就调用了第二个接受string类型的property表示的DbEntityEntry.Property重载。DbEntityEntry.Property(string)在经过了一系列其他的内部调用之后,到了System.Data.Entity.Internal.InternalEntityEntry.Property(...):

private InternalPropertyEntry Property(InternalPropertyEntry parentProperty, string propertyName, IList<string> properties, Type requestedType, bool requireComplex)
{
    bool flag = properties.Count > 1;
    Type type = flag ? typeof(object) : requestedType;
    Type declaringType = (parentProperty != null) ? parentProperty.EntryMetadata.ElementType : this.EntityType;
    PropertyEntryMetadata metadata = this.ValidateAndGetPropertyMetadata(properties[0], declaringType, type);
    if ((metadata == null) || ((flag || requireComplex) && !metadata.IsComplex))
    {
        if (flag)
        {
            throw Error.DbEntityEntry_DottedPartNotComplex(properties[0], propertyName, declaringType.Name);
        }
        throw (requireComplex ? Error.DbEntityEntry_NotAComplexProperty(properties[0], declaringType.Name) : Error.DbEntityEntry_NotAScalarProperty(properties[0], declaringType.Name));
    }
    InternalPropertyEntry entry = (InternalPropertyEntry) metadata.CreateMemberEntry(this, parentProperty);
    if (!flag)
    {
        return entry;
    }
    return this.Property(entry, propertyName, properties.Skip<string>(1).ToList<string>(), requestedType, requireComplex);

}

从标黄的语句中不难发现,这个函数也是递归调用,并且将多层的property一一解析。 System.Data.Entity.Internal.InternalEntityEntry.Property函数返回InternalPropertyEntry类型的对象。这个对象又被返回给DbPropertyEntry.Create方法,Create方法又调用了InternalPropertyEntry.CreateDbMemberEntry:

public override DbMemberEntry<TEntity, TProperty> CreateDbMemberEntry<TEntity, TProperty>() where TEntity: class
{
    if (!this.EntryMetadata.IsComplex)
    {
        return new DbPropertyEntry<TEntity, TProperty>(this);
    }
    return new DbComplexPropertyEntry<TEntity, TProperty>(this);
}

这里的逻辑很简单,将property分成一般的属性或复杂类型的属性,分别处理之。分析到这里,大家应该也发现了DbPropertyEntry中大部分信息都保存在其内部的InternalPropertyEntry对象里。这样的设计在解析EntityFramework.dll时,我们会经常看到。

呼!这篇文章不是一口气写完的了,这几天挺忙的,不过每天添几笔,可能思路有些混乱,大家见谅,哈哈!还是那句话,希望对您学习EF有点帮助吧!

PS1:这里为大家带来一个好消息:微软一站式实例代码库(Microsoft All-In-One Code Framework)即日起正式迁移至MSDN代码库了,新的平台会帮您更轻松地解决开发难题、节省更多时间、获得更友好的用户体验。本人作为这个项目的元老,见到我们已拥有600多个经典的代码实例,甚感欣慰啊!  更详细信息,请看http://msdn.microsoft.com/zh-cn/hh124104.aspx?ocid=ban-f-cn-loc-OC201104-MSDN

之后我将尽力为大家带来更多有关EF的代码实例以及相关的介绍!

PS2:同事开发了一个很cool的MSDN论坛桌面小工具,绝对给力!欢迎使用!(我也出了不少力啊

也欢迎到MSDN中文论坛ADO.NET与LINQ论坛来提问EF的问题啊,可以试试直接报我的名字Michael Sun,哈哈!

如需转发请注明原文出处,谢谢: http://www.cnblogs.com/LingzhiSun/archive/2011/04/13/EF41_WokingWithProperties.html

转载于:https://www.cnblogs.com/LingzhiSun/archive/2011/04/13/EF41_WokingWithProperties.html

Entity Framework 4.1 DbContext使用记之三——如何玩转实体的属性值?相关推荐

  1. Entity Framework 4.1 DbContext使用记之二——如何玩转本地实体? DbSet.Local属性的使用与实现...

    说好为大家带来一系列的文章,现在就写第二篇.开始之前,再啰嗦两句,EF4.1 RTW版本已经发布:http://www.microsoft.com/downloads/en/details.aspx? ...

  2. Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作

    Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作 Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作 1>. 创建一个控制台程序 2 ...

  3. Entity Framework 6 Recipes 2nd Edition(13-2)译 - 用实体键获取一个单独的实体

    问题 不管你用DBFirst,ModelFirst或是CodeFirst的方式,你想用实体键获取一个单独的实体.在本例中,我们用CodeFirst的方式. 解决方案 假设你有一个模型表示一个Paint ...

  4. ABP官方文档翻译 9.2 Entity Framework Core

    Entity Framework Core 介绍 DbContext 配置 在Startup类中 在模块PreInitialize方法中 仓储 默认仓储 自定义仓储 应用程序特定基础仓储类 自定义仓储 ...

  5. Entity Framework Code First关系映射约定

    本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...

  6. 初识Entity Framework CodeFirst(3)

    前两回合,我们讨论学习了如何采用Entity Framework在没有数据库的情况下自己写一些实体类,然后通过CodeFirst反向生成对应的数据库.通过CodeFirst,我们摆脱了edmx文件,没 ...

  7. 全自动迁移数据库的实现 (Fluent NHibernate, Entity Framework Core)

    在开发涉及到数据库的程序时,常会遇到一开始设计的结构不能满足需求需要再添加新字段或新表的情况,这时就需要进行数据库迁移. 实现数据库迁移有很多种办法,从手动管理各个版本的ddl脚本,到实现自己的mig ...

  8. 什么是Entity Framework

    什么是Entity Framework Entity Framework是一个对象关系映射O/RM框架. Entity Framework让开发者可以像操作领域对象(domain-specific o ...

  9. 在Entity Framework中使用存储过程(一):实现存储过程的自动映射

    之前给自己放了一个比较长的假期,在这期间基本上没怎么来园子逛.很多朋友的留言也没有一一回复,在这里先向大家道个歉.最近一段时间的工作任务是如何将ADO.NET Entity Framework 4.0 ...

最新文章

  1. 正确设置php-fpm和nginx防止网站被黑
  2. SQL Server 2012入门T-SQL基础篇:(2)SQL的两个基础概念
  3. OpenCASCADE:拓扑 API之特征
  4. Ubuntu 挂载新磁盘
  5. python动态变量名_python实现可变变量名方法详解
  6. Python(42)_文件操作
  7. XBAP 承载 Silverlight 的示例
  8. 另存为fdf或xps加载项_2007 Microsoft Office加载项:Microsoft另存为PDF或XPS
  9. 根据旋转角计算欧拉角 (Computing Euler angles from a rotation matrix)
  10. 进程线程(六) 深度睡眠 和 浅度睡眠
  11. C++标准库分析总结(一)——<标准库简介>
  12. 【FCC前端教程】28关学会HTML与HTML5基础
  13. 区块链溯源:重塑咖啡产业链
  14. 中关村创业大街是贵重金属垃圾场,我捡到一只产品经理
  15. 信息安全技术期末总复习
  16. 软件设计师教程(四)程序设计语言基础知识
  17. 张宇高数 第一讲 极限(思维导图)
  18. BUI前端框架·导航栏
  19. IM 聊天页面下拉加载更多--类似微信顺滑展示
  20. 201615105027张金文

热门文章

  1. mysql 递归实现树形_Mysql实现树形递归查询
  2. ubuntu 在线安装php,ubuntu在线安装LNMP
  3. java servlet 转发和重定向_JavaWeb(一)Servlet中乱码解决与转发和重定向的区别
  4. 于小c三国语言_云顶之弈:三国成最强打工羁绊 校长教学顺滑转九五
  5. as3调用java_关于openamf我用as3链接java程序,并调用相关的方法,但是能够连上,却不能够调用是怎么回事...
  6. 在页游中LUA的应用(1)
  7. C++ primer第六章6.4函数的学习 之函数的重载
  8. 科目三道路考试技巧流程详解
  9. 给定a和n,计算a+aa+aaa+a...a(n个a)的和(大数据处理)
  10. 解决:mysql 连接报错 Authentication plugin ‘caching_sha2_password‘cannot be loaded