利用泛型与反射更新实体(ADO.NET Entity Framework)
自从ADO.NET Entity Framework面世以来,受到大家的热捧,它封装了大量代码生成的工具,用户只需要建立好实体之间的关系,系统就是会为用户自动成功了Add、Delete、CreateObject、Attach、ToList......等等方法,这些方法基本上已经包含获取、删除、插入等基本方法,使用起来非常方便。只是在实体的更新上,由于LINQ面向的是泛型对象T,所以每个对象的更新方法都要由用户自动编辑。有见及此,下面在下利用反射方法,创建了一个更新工具,此工具可以更新ObjectContext里面的任意一个实体或者多个关联实体。
一、简单介绍反射
反射是一个程序集发现及运行的过程,通过反射可以得到*.exe或*.dll等程序 集内部的信息。使用反射可以看到一个程序集内部的接口、类、方法、字段、属性、特性等等信息。在System.Reflection命名空间内包含多个反 射常用的类,下面表格列出了常用的几个类。(详细的资料请参考“反射的奥妙”)
类型 | 作用 |
Assembly | 通过此类可以加载操纵一个程序集,并获取程序集内部信息 |
EventInfo | 该类保存给定的事件信息 |
FieldInfo | 该类保存给定的字段信息 |
MethodInfo | 该类保存给定的方法信息 |
MemberInfo | 该类是一个基类,它定义了EventInfo、FieldInfo、MethodInfo、PropertyInfo的多个公用行为 |
Module | 该类可以使你能访问多个程序集中的给定模块 |
ParameterInfo | 该类保存给定的参数信息 |
PropertyInfo | 该类保存给定的属性信息 |
二 、实体与上下文的关系
每个实体值都会包含在上下文中, 当您从客户端回收到实体时, 可以比较与上下文中的该实体的版本更新的实体,并应用适当的更改。值得注意的是,上下文会必须KEY找到实体对像,然后对每一个属性逐个赋值。如果想对实体对象直接赋值,那么KEY那将会改变,系统将无法从上下文中找到该对象。
三、开发实例
在为一个项目建立关系图时,都会为多个实体建立关系,在更新实体时往往需要把导航属性一同更新,这使得更新方法更为繁琐。比如在觉见的订单管理项目中,在更新订单Order的同时,必须把订单对应的OrderItem对象实现同步更新。为了简化代码,在下利用反射原理建立一个了特定类UpdateHelp,利用这个类可以更新ObjectContext里面的多个关系对象。
其原理在于系统使用GetIntrinsicObj(EntityObject)方法,根据输入实体(obj)的KEY在上下文中获取对应的实体对象(intrinsic),然后使用UpdateIntrinsticObj(Object)方法,利用PropertyInfo遍历实体的每个属性,把输入的实体对象(obj)的每个属性都赋值给上下文的实体对象(intrinsic)。最特别的地方在于当遇到导航属性的时候,使用了递归算法,重复调用UpdateIntrinsticObj(object)方法为导航属性赋值。当遇到一对多或者多对多关系的时候,导航属性将会是是一个List<T>对象,方法中CloneNavigationProperty是为单个对象赋值,而CloneNavigationPropertyEntityCollection方法是为多个对象赋值。
public class UpdateHelp:IDisposable {//记录已经复制过的实体类,避免重复加载 private IList<Type> listType = new List<Type>();private BusinessContext _context; public UpdateHelp(BusinessContext context) { _context = context; } public void Dispose() { _context.Dispose(); } //更新普通属性 private void CloneProperty(PropertyInfo propertyInfo,object intrinsicObj,object newObj) {var data = propertyInfo.GetValue(newObj, null); propertyInfo.SetValue(intrinsicObj, data, null); } //更新普通导航属性 private void CloneNavigationProperty(PropertyInfo propertyInfo,object intrinsicObj,object newObj) {var data = propertyInfo.GetValue(newObj, null);object dataClone = UpdateIntrinsticObj(data); propertyInfo.SetValue(intrinsicObj, dataClone, null); } //更新返回值为集合EntityCollection<TEntity>的导航属性 private void CloneNavigationPropertyEntityCollection(PropertyInfo propertyInfo, object intrinsicObj, object newObj) {//获取参数newObj中的对象集合 IEnumerable<object> newData = propertyInfo.GetValue(newObj, null) as IEnumerable<object>;//获取上下文中匹配的原对象集合 var intrinsicData = propertyInfo.GetValue(intrinsicObj, null);//利用EntityCollection<TEntity>类的扩展方法Add在原集合中加入新对象 var addMethod = intrinsicData.GetType().GetMethod("Add");foreach (object obj in newData) { Object objClone = UpdateIntrinsticObj(obj); addMethod.Invoke(intrinsicData, new object[] { objClone }); } } //获取上下文中待更新的原对象 private object GetIntrinsicObj(EntityObject entity) { Object intrinsicObj;//根据输入对象的EntityKey判断该对象是已有值还是新建值//若是已有值即从上下文中获取对应值,若是新建值即反射生成一个新对象 if (entity.EntityKey.EntityKeyValues != null) intrinsicObj = _context.GetObjectByKey(entity.EntityKey);else intrinsicObj = Activator.CreateInstance(entity.GetType()); return intrinsicObj; } //更新上下文中的原对象,返回值为更新后的原对象 public object UpdateIntrinsticObj(Object obj) {//记录已经复制过的实体类,避免重复加载 listType.Add(obj.GetType());//获取上下文中的原对象 Object intrinsicObj=GetIntrinsicObj(obj as EntityObject);//更新原对象的每个一个属性//把原对象intrinsicObj的每一个属性设置为与obj对象相等 foreach (PropertyInfo propertyInfo in obj.GetType().GetProperties()) {//若listType里面包含些类型,证明此实体类已经更新过 if (!listType.Contains(propertyInfo.PropertyType) && propertyInfo.CanWrite && propertyInfo.Name != "EntityKey"&& propertyInfo.PropertyType.Name != "EntityReference`1") {//若为导航属性则需要使用此方法更新 if (propertyInfo.GetCustomAttributes(typeof(EdmRelationshipNavigationPropertyAttribute), false).Count() != 0) {//若导航属性返回值为集合则使用此方法 if (propertyInfo.PropertyType.Name == "EntityCollection`1") CloneNavigationPropertyEntityCollection(propertyInfo, intrinsicObj, obj);else//若导航属性为普通对象则使用以下方法 CloneNavigationProperty(propertyInfo, intrinsicObj, obj); }else //若为普通属性则使用以下方法 CloneProperty(propertyInfo, intrinsicObj, obj); } }return intrinsicObj; } }
在完成更新操作后,再加上LingHelp类,就可以利用它完成大部的数据处理问题,大家可以建立main测试一下。
public class LinqHelp:IDisposable {private BusinessContext _context;private UpdateHelp _updateHelp; public LinqHelp() { _context = new BusinessContext(); _updateHelp = new UpdateHelp(_context); } public LinqHelp(BusinessContext context) { _context = context; _updateHelp = new UpdateHelp(context); } public void Dispose() { _context.Dispose(); } public int Add<T>(T entity) where T : EntityObject {int n = -1; Transaction transaction = Transaction.Current;try { _context.AddObject(entity.GetType().Name, entity); n = _context.SaveChanges(); }catch (Exception ex) { Business.Common.ExceptionManager.DataException.DealWith(ex); transaction.Rollback(); }return n; } public int Update<T>(ref T entity) where T:EntityObject {int n = -1; Transaction transaction = Transaction.Current;try { EntityObject returnObj = this._updateHelp.UpdateIntrinsticObj(entity) as EntityObject; n = _context.SaveChanges(); entity = _context.GetObjectByKey(entity.EntityKey) as T; }catch (Exception ex) { Business.Common.ExceptionManager.DataException.DealWith(ex); transaction.Rollback(); }return n; } public List<T> GetList<T>(string name) where T:EntityObject {......} ........ } public class OrderRepository {private LinqHelp _linqHelp; public OrderRepository() { _linqHelp = new LinqHelp(); } public int AddOrder(Order order) {..........} .............. public int UpdateOrder(Order order) {return _linqHelp.Update<Order>(ref order); } }
public class PersonRepository {......} class Program {static void Main(string[] args) { Test1(); Console.ReadKey(); } public static void Test1() {using (BusinessContext context = new BusinessContext()) { context.ContextOptions.LazyLoadingEnabled = true;var order = context.Order.First(); order.Person.Address = "北京路1号"; OrderRepository orderRepository = new OrderRepository(); orderRepository.UpdateOrder(order); } } public static void Test2() { using (BusinessContext context = new BusinessContext()) { Person person = context.Person.First(); Order order = new Order(); order.OrderNumber = "2A34313344"; OrderItem orderItem = new OrderItem(); orderItem.Goods = "555"; orderItem.Count = 8; orderItem.Price = 2.5; order.OrderItem.Add(orderItem); person.Order.Add(order); PersonRepository personRepository = new PersonRepository(); personRepository.UpdatePerson(person); Console.Write(person.Order.First().ID + person.Order.First().OrderItem.First().ID); } } }
四、性能问题
由于过度使用反射会使系统的性能下降,所以需要注意此更新方法的使用范围。一般此反射更新只会使用在小型的项目当中,如果在大中型项目内使用,将会在性能上负出代价。由于时间有限,而且没有经过大量的测试,有不足之处请点评。
对.NET开发有兴趣的朋友请加入博客园讨论小组“.NET高级编程” 一起探讨!
转载于:https://www.cnblogs.com/wangyajun/archive/2012/04/26/2471788.html
利用泛型与反射更新实体(ADO.NET Entity Framework)相关推荐
- ADO.NET Entity Framework Beta2(五)/快速入门(实体框架)
This quickstart illustrates a series of tasks that support the topics in Getting Started with the En ...
- 转载:ADO.NET Entity Framework 试水系列索引(2008/9/9更新,本系列结束)
Visual Studio 2008发布时,微软给我们带来了LinQ to SQL,解决所谓"阻抗不匹配"问题.最近,随着Visual Studio 2008 SP1的正式发布,又 ...
- ADO.NET Entity Framework 简介
一直对EF都是一知半解的,没有系统的了解过EF有什么样的功能,有什么具体的好处.在接下来的文章中会详细介绍EF.以下是参考MSDN上的文章,翻译并加以自己的理解得出的. ADO.NET Entity ...
- ADO.NET Entity Framework 基本概述
时间过得很快转眼间VS已经2010版了,在4月12日将会正式发布VS 2010 ADOEF 做为.Net 4.0 中被微软推荐的ORM框架,相比.Net 3.5 sp1 已经得到了进一步的增强,使用它 ...
- ADO.NET Entity Framework 入门示例向导(附Demo程序下载)
ADO.NET Entity Framework 入门示例向导(附Demo程序下载) ADO.NET Entity Framework 是.Net Framework 3.5 SP1 引入的实体框架, ...
- ADO.NET Entity Framework 深入分析, Part 5
ADO.NET Entity Framework 深入分析, Part 5 前面的Part 1-4的文章,介绍了Entity Data Model.Entity SQL.ObjectQuery.Ent ...
- EF架构~了解一下,ADO.NET Entity Framework
回到目录 以下文章部分来自百度百科 背景 长久以来,程序设计师和数据库总是保持着一种微妙的关系,在商用应用程序中,数据库一定是不可或缺的元件,这让程序设计师一定要为了连接与访问数据库而去 学习 SQL ...
- Entity Framework (EF)/Linq To entity/ ESQL(entity sql)区别 ADO.NET Entity Framework:来自微软官方的ORM框架
长久以来,程序员和数据库总是保持着一种微妙的关系,在商用应用程序中,数据库一定是不可或缺的元件,这让程序员一定要为了连接与访问数据库而去学习 SQL 指令,至少对于我而言,我觉得这是一个很不爽的事情. ...
- 实体框架(Entity Framework)简介
实体框架(Entity Framework)简介 实体框架(Entity Framework)简介 简称EF,与ADO.NET关系 ADO.NET Entity Framework 是微软以 ADO. ...
最新文章
- Xamarin.Android之Fragment Walkthrough
- 9.3 域名解析与网页爬虫
- 1008 Elevator (20 分)【难度: 简单 / 知识点: 模拟】
- 使用 Bamboo 构建项目的 CICD 过程文档
- Jquery- 错误消息Date未定义,String未定义
- Qt消息机制和事件概述(一)
- AJAX,只是一种过渡技术吗?
- MFC程序打开文件对话框出错的问题解决
- python学习笔记4(对象/引用;多范式; 上下文管理器)
- 基于Spring Boot 的Blog开发(一)
- mac搜索不到wifi wtg_如何设置隐藏wifi 防止蹭网隐藏wifi方法【详解】
- Algs4-1.1.6下面这段程序会打印出什么
- python设计模式之MVC
- google的视频下载插件
- 苹果新专利:紧急情况下可用指纹或特定输入操作悄悄呼救
- 职业规划,如何月入1万、3万、5万、10万?
- STM32寄存器配置USART1串口及USART->BRR值的计算
- Latex编辑数学符号和希腊字母的方法
- Python题库——题目
- live555源代码分析
热门文章
- rman一致性备份oracle数据库可以在非归档模式下么,探索ORACLE之RMAN_03一致性备份...
- html5两个静态页面传值,如何使用HTML5Viewer 进行参数传递
- php+select为空,SELECT时候,如何处理某字段空值?
- python元组是有序还是无序_python-03 元组和字典
- grpc python stream_Python gRPC笔记
- spark.network.timeout参数入门
- GeoHash入门及应用
- SparkSession对象
- 【转】契约测试的必要性
- nginx官方模块之http_random_index_module