自从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高级编程” 一起探讨!

.NET基础篇——利用泛型与反射更新实体(ADO.NET Entity Framework)(转)的更多相关文章

  1. 利用泛型和反射,管理配置文件,把Model转换成数据行,并把数据行转换成Model

    利用泛型和反射,管理配置文件,把Model转换成数据行,并把数据行转换成Model   使用场景:网站配置项目,为了便于管理,网站有几个Model类来管理配置文件, 比如ConfigWebsiteMo ...

  2. ASP.NET MVC深入浅出系列(持续更新) ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结(3)(C#6.0和C#7.0新语法) 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借

    ASP.NET MVC深入浅出系列(持续更新)   一. ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模 ...

  3. Java 基础篇之泛型

    背景 在没有泛型前,一旦把一个对象丢进集合中,集合就会忘记对象的类型,把所有的对象都当成 Object 类型处理.当程序从集合中取出对象后,就需要进行强制类型转换,这种转换很容易引起 ClassCas ...

  4. 【Unity|C#】基础篇(10)——泛型(Generic)/ 泛型约束条件(where)

    [学习资料] <C#图解教程>(第17章):https://www.cnblogs.com/moonache/p/7687551.html 电子书下载:https://pan.baidu. ...

  5. Silverlight通过Wcf Data Service访问数据库之ADO.NET Entity Framework篇

  6. 转:.NET基础篇——反射的奥妙

    反射是一个程序集发现及运行的过程,通过反射可以得到*.exe或*.dll等程序集内部的信息.使用反射可以看到一个程序集内部的接口.类.方法.字段.属性.特性等等信息.在System.Reflectio ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 目录篇 -持续更新

    为了方便大家的阅读和学习,也是响应网友的建议,在这里为这个系列做一个目录.在目录开始这前,我先来回答之前遇到的几个问题. 1.为什么要学习EF? 这个问题很简单,项目需要.这不像学校,没人强迫你学习! ...

  8. Entity Framework 实体框架的形成之旅--利用Unity对象依赖注入优化实体框架(2)

    在本系列的第一篇随笔<Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)>中介绍了Entity Framework 实体框架的一些基础知识,以及构建 ...

  9. Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)

    很久没有写博客了,一些读者也经常问问一些问题,不过最近我确实也很忙,除了处理日常工作外,平常主要的时间也花在了继续研究微软的实体框架(EntityFramework)方面了.这个实体框架加入了很多特性 ...

随机推荐

  1. 【NOIP2016提高组A组7.16】大鱼海棠

    题目 椿是掌管海棠花的少女,她所在的世界不为人们所知,他们的天空就是人类的海底.生活在那个世界里的他们不是人,也不是鱼,而是其他人,掌管着人间的规律. 按照他们的习俗,在16岁那年,椿变为一条海豚到人 ...

  2. 【bzoj3295】[Cqoi2011]动态逆序对

    题目描述: 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆 ...

  3. Leetcode 7. Reverse Integer(水)

    7. Reverse Integer Easy Given a 32-bit signed integer, reverse digits of an integer. Example 1: Inpu ...

  4. 【PowerOJ1741&网络流24题】最长递增子序列问题(最大流)

    题意: 思路: [问题分析] 第一问时LIS,动态规划求解,第二问和第三问用网络最大流解决. [建模方法] 首先动态规划求出F[i],表示以第i位为开头的最长上升序列的长度,求出最长上升序列长度K. ...

  5. 学习日记16、easyui editor datagrid 动态绑定url

    首先获取easyui当前的editor,然后看代码就可以了 var Index = $("#draw").datagrid("appendRow", { Ext ...

  6. 170907-关于JavaWeb的题

    1. 答案是B.D Servlet 通过调用 init () 方法进行初始化. Servlet 调用 service() 方法来处理客户端的请求. Servlet 通过调用 destroy() 方法终 ...

  7. SpringBoot 集成 Spring Session

    SpringBoot 集成 Spring Session 应该讲解清楚,为什么要使用 Redis 进行 Session 的管理. Session 复制又是什么概念. Spring Session 在汪 ...

  8. 基于自定义的动态数组实现一个栈(Java语言)

    关于动态数组,参见我的上一篇关于动态数组的博文https://www.cnblogs.com/inu6/p/11717129.html 1.什么是栈? (1)只能从一端添加元素,也只能从一端取出元素, ...

  9. 测试markdorn

    专业主义 描述:这本书着重阐释了真正的专家必须具备的四种能力:**先见能力**.构思能力.讨论的能力.适应矛盾的能力,以丰富的案例和深刻的洞见警示人们重新思考专业的内涵与效用,培养并吸纳专业人才. 状 ...

  10. 关于Java协变性的思考

    简而言之,如果A IS-A B,那么A[] IS-A B[]. 举例:现在有类型Person.Employee和Student.Employee 是一个(IS-A) Person,Student是一个 ...