【问题标题】:EF5 How to get list of navigation properties for a domain objectEF5 如何获取域对象的导航属性列表
【发布时间】:2013-07-26 16:44:07
【问题描述】:

我正在开发一个 EF 5 Code-First 解决方案,我正在尝试使用存储库模式使用修改过的实体来更新现有实体:

    public void UpdateValues(T originalEntity, T modifiedEntity)
    {
        _uow.Context.Entry(originalEntity).CurrentValues.SetValues(modifiedEntity);

我的简化域对象如下所示:

public class PolicyInformation : DomainObject
{
     //Non-navigation properties
     public string Description {get;set;}
     public bool Reinsurance {get;set;}
     ...
     //Navigation properties
     LookupListItemFormType FormType {get;set;}
     ...
}

我遇到的问题是CurrentValues.SetValues(modifiedEntity); 方法似乎只更新标量和复杂类型属性,但不更新导航属性。 我已经看到很多人发生这种情况,但仍然不知道为什么会这样。但是,我发现如果在执行 UpdateValues 后手动设置这些导航属性,一切正常:

            _policyInfoRepo.UpdateValues(existingPolicyInfo, info);
            existingPolicyInfo.FormType = info.FormType;

问题是,因为我使用的是通用存储库:如何获取域对象中的导航属性列表?也许通过 linq、反射或任何其他方式,以便我的存储库中的更新方法可以循环遍历它们并自动设置它们?

类似这样的:

    public void UpdateValues(T originalEntity, T modifiedEntity)
    {
        //Set non-nav props
        _uow.Context.Entry(originalEntity).CurrentValues.SetValues(modifiedEntity);
        //Set nav props
        var navProps = GetNavigationProperties(originalEntity);
        foreach(var navProp in navProps)
        {
           //Set originalEntity prop value to modifiedEntity value
        }

谢谢!

【问题讨论】:

    标签: entity-framework c#-4.0 navigation-properties


    【解决方案1】:

    我使用 EF6 编写了以下内容,但我相信它都与 EF5 兼容。代码背后的总体思路是使用a System.Data.Metadata.Edm中的优秀类来获取导航属性,并在这些属性名称上使用反射来获取对象的真实属性进行更新。

    我想让我的示例尽可能通用但完整。在提问者的情况下,他显然只是将“context”替换为“_uow.Context”。

    public class MyClass<T> where T : class //T really needs to always be an entity, 
                                            //but I don't know a general parent type
                                            //for that. You could leverage partial classes
                                            //to define your own type.
    {
        public MyEntities context { get; set; }
    
        public void UpdateValues(T originalEntity, T modifiedEntity)
        {
            //Set non-nav props
            context.Entry(originalEntity).CurrentValues.SetValues(modifiedEntity);
            //Set nav props
            var navProps = GetNavigationProperties(originalEntity);
            foreach (var navProp in navProps)
            {
                //Set originalEntity prop value to modifiedEntity value
                navProp.SetValue(originalEntity, navProp.GetValue(modifiedEntity));                
            }
        }
    
        public List<System.Reflection.PropertyInfo> GetNavigationProperties(T entity)
        {
            List<System.Reflection.PropertyInfo> properties = new List<System.Reflection.PropertyInfo>();
            //Get the entity type
            Type entityType = entity.GetType();
            //Get the System.Data.Entity.Core.Metadata.Edm.EntityType
            //associated with the entity.
            var entitySetElementType = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)context).ObjectContext.CreateObjectSet<T>().EntitySet.ElementType;
            //Iterate each 
            //System.Data.Entity.Core.Metadata.Edm.NavigationProperty
            //in EntityType.NavigationProperties, get the actual property 
            //using the entityType name, and add it to the return set.
            foreach (var navigationProperty in entitySetElementType.NavigationProperties)
            {
                properties.Add(entityType.GetProperty(navigationProperty.Name));
            }
            return properties;
        }
    }
    

    【讨论】:

      【解决方案2】:

      啊,LINQ(和using)的荣耀:

      public List<PropertyInfo> GetNavigationProperties(T entity)
      {
          var t = entity.GetType();
          var elementType = ((IObjectContextAdapter)context).ObjectContext.CreateObjectSet<T>().EntitySet.ElementType;
          return elementType.NavigationProperties.Select(np => entityType.GetProperty(np.Name)).ToList();
      }
      

      这也可以通过static 方法实现,具有以下签名:

      public static List<PropertyInfo> GetNavigationProperties<T>(DbContext context)
      {
          var t = typeof(T);
          ...
      

      【讨论】:

        【解决方案3】:

        根据 Zev 的回答:

        public List<PropertyInfo> GetNavigationProperties<T>(DbContext context) where T : class
        {
            var entityType = typeof(T);
            var elementType = ((IObjectContextAdapter)context).ObjectContext.CreateObjectSet<T>().EntitySet.ElementType;
            return elementType.NavigationProperties.Select(property => entityType.GetProperty(property.Name)).ToList();
        }
        

        【讨论】:

        • 我们应该知道CrowdWorxEntities 是什么吗?
        【解决方案4】:
            public static T Clone<T>(this T entity) where T : class
            {
                var type = entity.GetType();
                var clone = Activator.CreateInstance(type);
        
                var navigationProperties = type.GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty | BindingFlags.DeclaredOnly);
        
                foreach (var property in type.GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty))
                {
                    //if (property.Module.ScopeName == "EntityProxyModule") continue;
                    if (navigationProperties.Contains(property)) continue;
        
                    if (property.CanWrite)
                    {
                        property.SetValue(clone, property.GetValue(entity, null), null);
                    }
                }
        
                return (T)clone;
            }
        

        只有导航属性是直接在动态代理上定义的,所以使用 BindingFlags.DeclaredOnly 会将它们过滤掉。

        替代(已注释掉)方法使用 property.Module.ScopeName 区分导航属性,对于导航属性,此值将是“EntityProxyModule”,而对于其他属性,此值将是您定义的项目的 dll 名称EF 代码中的第一个域类。

        我使用这个 Clone 方法来克隆 EF 代码优先域对象,为 Json 序列化做好准备(避免循环引用),但作者关于如何获取导航属性列表的查询包含在其中。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-01
          • 2021-11-16
          • 1970-01-01
          • 2010-09-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多