C#综合揭秘——利用泛型与反射更新实体(ADO.NET Entity Framework) C#综合揭秘——利用泛型与反射更新实体(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里面的多个关系对象。

\" src=

 

其原理在于系统使用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; 
       } 
   } 
 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); 
        } 
    } 

    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);
            }
        }
    }


四、性能问题

由于过度使用反射会使系统的性能下降,所以需要注意此更新方法的使用范围。一般此反射更新只会使用在小型的项目当中,如果在大中型项目内使用,将会在性能上负出代价。(由于时间有限,而且没有经过大量的测试,有不足之处请点评

posted @ 2012-05-28 23:36  蓝雨.Berners  阅读(498)  评论(0编辑  收藏  举报