转载Entity Framework 4.1 DbContext使用记之三——如何玩转实体的属性值?
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 > ); 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();
// 得到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();
// 改变对应的实体的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 Expressionstring 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(); 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(); var person1 = new Person { PersonID = , Name = "Michael" }; var person2 = new Person { PersonID = , 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 > ; Type type = flag ? typeof(object) : requestedType; Type declaringType = (parentProperty != null) ? parentProperty.EntryMetadata.ElementType : this.EntityType; PropertyEntryMetadata metadata = this.ValidateAndGetPropertyMetadata(properties[], declaringType, type); if ((metadata == null) || ((flag || requireComplex) && !metadata.IsComplex)) { if (flag) { throw Error.DbEntityEntry_DottedPartNotComplex(properties[], propertyName, declaringType.Name); } throw (requireComplex ? Error.DbEntityEntry_NotAComplexProperty(properties[], declaringType.Name) : Error.DbEntityEntry_NotAScalarProperty(properties[], 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时,我们会经常看到。
原文出处: http://www.cnblogs.com/LingzhiSun/archive/2011/04/13/EF41_WokingWithProperties.html
转载原出处:这里为大家带来一个好消息:微软一站式实例代码库(Microsoft All-In-One Code Framework)即日起正式迁移至MSDN代码库了,新的平台会帮您更轻松地解决开发难题、节省更多时间、获得更友好的用户体验。已拥有600多个经典的代码实例, 更详细信息,请看http://msdn.microsoft.com/zh-cn/hh124104.aspx?ocid=ban-f-cn-loc-OC201104-MSDN
转载Entity Framework 4.1 DbContext使用记之三——如何玩转实体的属性值?的更多相关文章
- 转载Entity Framework全面教程
转载原地址:http://www.cnblogs.com/lsxqw2004/archive/2009/05/31/1495240.html#_Toc228672754 预备知识 2 LINQ技 ...
- Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作
Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作 1>. 创建一个控制台程序2>. 添加一个 ADO.NET实体数据模型,选择对应的数据库与表(Studen ...
- 转载Entity Framework 5.0(EF first)中的添加,删除,修改,查询,状态跟踪操作
转载原出处:http://www.cnblogs.com/kenshincui/p/3345586.html Entity Framework将概念模型中定义的实体和关系映射到数据源,利用实体框架可以 ...
- [转载] - Entity Framework 性能优化建议
1.对象管理机制-复杂为更好的管理模型对象,EF提供了一套内部管理机制和跟踪对象的状态,保存对象一致性,使用方便,但是性能有所降低. 2.执行机制-高度封装在EF中,所有的查询表达式都会经过语法分析. ...
- 《Entity Framework 6 Recipes》中文翻译系列 (9) -----第二章 实体数据建模基础之继承关系映射TPH
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-10 Table per Hierarchy Inheritance 建模 问题 ...
- Entity Framework 6 Recipes 2nd Edition(13-2)译 -> 用实体键获取一个单独的实体
问题 不管你用DBFirst,ModelFirst或是CodeFirst的方式,你想用实体键获取一个单独的实体.在本例中,我们用CodeFirst的方式. 解决方案 假设你有一个模型表示一个Paint ...
- 《Entity Framework 6 Recipes》中文翻译系列 (8) -----第二章 实体数据建模基础之继承关系映射TPT
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-8 Table per Type Inheritance 建模 问题 你有这样一 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (10) -----第二章 实体数据建模基础之两实体间Is-a和Has-a关系建模、嵌入值映射
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-11 两实体间Is-a和Has-a关系建模 问题 你有两张有Is-a和Has-a ...
- Entity Framework 6 Recipes 2nd Edition(12-8)译 -> 重新获取一个属性的原始值
12-8. 重新获取一个属性的原始值 问题 在实体保存到数据库之前,你想重新获取属性的原始值 解决方案 假设你有一个模型 (见 Figure 12-11) 表示一个员工( Employee),包含工资 ...
随机推荐
- 【BZOJ 1045】 1045: [HAOI2008] 糖果传递
1045: [HAOI2008] 糖果传递 Description 有n个小朋友坐成一圈,每人有ai个糖果.每人只能给左右两人传递糖果.每人每次传递一个糖果代价为1. Input 第一行一个正整数n& ...
- socket关闭动作以及socket状态的总结
主要部分,四次握手: 断开连接其实从我的角度看不区分客户端和服务器端,任何一方都可以调用close(or closesocket)之类的函数开始主动终止一个连接.这里先暂时说正常情况.当调用close ...
- uva 11817 - Tunnelling the Earth
题意:从地球上的一个点到另一个点,求两点的球面距离和直线距离之差.假定地球是正球体,半径为6371009米. #include<iostream> #include<cmath> ...
- linux系统灵活运用灯[android课程3]
1,文件如何生成: ----- ---- (二),把hello例子贴过来后,编译问题: 在编译Android 4.0驱动的时候,使用到了proc_dir_entry结构体中的owner成员,但是编译的 ...
- GPS(1)核心API及3种列出LocationProvider的方法
GPS的常用API Android SDK为GPS提供了很多API,其中LocationManager类是这些API的核心.所有与GPS相关的操作都由LocationManager对象及其派生的对象完 ...
- Android开发之R文件丢失
在进行android开发的过程中,不知道怎么回事,代码中出现R代码有红色波浪线了,于是进行了clean,结果还是有红色波浪线,然后就重启了eclipse,重启以后还是这个样子,随后发现工程的R文件丢失 ...
- input之placeholder与行高的问题。
我们实现一个输入框的视觉的时候为了保持其各种各样的兼容性: 1.鼠标要跟文字一样高度. 2.文字要居中对齐. 3.还要有placeholder 第一个目标,当实现一个高度为40像素的高度输入框时,为了 ...
- [2015编程之美] 资格赛C
#1150 : 基站选址 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 需要在一个N × M的网格中建立一个通讯基站,通讯基站仅必须建立在格点上. 网格中有A个用户,每个 ...
- JAX-RS入门 二 :运行
上一节,已经成功的定义了一个REST服务,并且提供了具体的实现,不过我们还需要把它运行起来. 在上一节的装备部分,列举了必须的jar(在tomcat中运行)和可选的jar(作为一个独立的应用程序运行) ...
- linux shell if 参数
shell 编程中使用到得if语句内判断参数 –b 当file存在并且是块文件时返回真 -c 当file存在并且是字符文件时返回真 -d 当pathname存在并且是一个目录时返回真 -e 当path ...