程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#綜合揭秘——利用泛型與反射更新實體(ADO.NET Entity Framework)

C#綜合揭秘——利用泛型與反射更新實體(ADO.NET Entity Framework)

編輯:C#入門知識

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


四、性能問題

由於過度使用反射會使系統的性能下降,所以需要注意此更新方法的使用范圍。一般此反射更新只會使用在小型的項目當中,如果在大中型項目內使用,將會在性能上負出代價。(由於時間有限,而且沒有經過大量的測試,有不足之處請點評)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved