由于这仍然是EF 6.1.1中的问题,因此我想根据他们的确切模型要求,提供另一个适合某些人的答案。总结问题:
您需要使用代理进行延迟加载。
延迟加载的属性标记为必需。
您想要修改和保存代理,而不必强制加载惰性引用。
当前的EF代理(都不是3个)不可能实现3,这在我看来是一个严重的缺陷。
在我的情况下,惰性属性的行为类似于值类型,因此当我们添加实体且从未更改时会提供其值。我可以通过保护其设置器而不提供更新方法来强制执行此操作,即必须通过构造函数创建它,例如:
var myEntity = new MyEntity(myOtherEntity);
MyEntity具有此属性:
public virtual MyOtherEntity Other { get; protected set; }
因此,EF将不会对此属性执行验证,但我可以确保它在构造函数中不为null。那是一种情况。
假设您不希望以这种方式使用构造函数,则仍可以使用自定义属性来确保验证,例如:
[RequiredForAdd]
public virtual MyOtherEntity Other { get; set; }
RequiredForAdd属性是一个自定义属性,继承自Attribute而不是RequiredAttribute。除了基本属性外,它没有其他属性或方法。
在我的数据库上下文类中,我有一个静态构造函数,该构造函数查找具有那些属性的所有属性:
private static readonly List<Tuple<Type, string>> validateOnAddList = new List<Tuple<Type, string>>();
static MyContext()
{
FindValidateOnAdd();
}
private static void FindValidateOnAdd()
{
validateOnAddList.Clear();
var modelType = typeof (MyEntity);
var typeList = modelType.Assembly.GetExportedTypes()
.Where(t => t.Namespace.NotNull().StartsWith(modelType.Namespace.NotNull()))
.Where(t => t.IsClass && !t.IsAbstract);
foreach (var type in typeList)
{
validateOnAddList.AddRange(type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(pi => pi.CanRead)
.Where(pi => !(pi.GetIndexParameters().Length > 0))
.Where(pi => pi.GetGetMethod().IsVirtual)
.Where(pi => pi.GetCustomAttributes().Any(attr => attr is RequiredForAddAttribute))
.Where(pi => pi.PropertyType.IsClass && pi.PropertyType != typeof (string))
.Select(pi => new Tuple<Type, string>(type, pi.Name)));
}
}
现在我们有了需要手动检查的属性列表,我们可以覆盖验证并手动验证它们,将任何错误添加到从基本验证器返回的集合中:
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
return CustomValidateEntity(entityEntry, items);
}
private DbEntityValidationResult CustomValidateEntity(DbEntityEntry entry, IDictionary<object, object> items)
{
var type = ObjectContext.GetObjectType(entry.Entity.GetType());
var result = base.ValidateEntity(entry, items);
if (entry.State != EntityState.Added || !validateOnAddList.Any(t => t.Item1 == type))
return result;
var propertiesToCheck = validateOnAddList.Where(t => t.Item1 == type).Select(t => t.Item2);
foreach (var name in propertiesToCheck)
{
var realProperty = type.GetProperty(name);
var value = realProperty.GetValue(entry.Entity, null);
if (value == null)
{
logger.ErrorFormat("Custom validation for RequiredForAdd attribute validation exception. {0}.{1} is null", type.Name, name);
result.ValidationErrors.Add(new DbValidationError(name, string.Format("RequiredForAdd validation exception. {0}.{1} is required.", type.Name, name)));
}
}
return result;
}
请注意,我只对验证Add感兴趣。如果您还想在Modify期间进行检查,则需要对属性进行强制加载,还是需要使用Sql命令检查外键值(难道该内容已不在上下文中)?
由于Required属性已被删除,因此EF将创建可为空的FK。为确保数据库完整性,可以在创建数据库后针对数据库运行的Sql脚本中手动更改FK。这将至少捕获具有空问题的“修改”。