我正在使用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
}
谢谢!
答案 0 :(得分:6)
我使用EF6编写了以下内容,但我相信它与EF5完全兼容。代码背后的一般思想是使用a System.Data.Metadata.Edm中的优秀类来获取导航属性,并对这些属性名称使用反射来获取对象的真实属性以进行更新。
我想让我的例子尽可能通用但又完整。在提问者的情况下,他显然会用“_uow.Context”替换“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;
}
}
答案 1 :(得分:5)
啊,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);
...
答案 2 :(得分:4)
根据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();
}
答案 3 :(得分:2)
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代码中的第一个域类。
我使用这个克隆方法克隆EF代码第一个准备好进行Json序列化的域对象(避免循环引用),但是作者关于如何获取导航属性列表的查询包含在其中。