由于这在 EF 6.1.1 中仍然是一个问题,我想我会提供另一个可能适合某些人的答案,具体取决于他们的确切模型要求。总结一下这个问题:
您需要使用代理进行延迟加载。
你延迟加载的属性标记为必填。
您想要修改和保存代理,而不必强制加载惰性引用。
当前的 EF 代理(其中任何一个)都无法实现 3,这在我看来是一个严重的缺陷。
在我的例子中,惰性属性的行为类似于值类型,因此它的值是在我们添加实体时提供的,并且从不更改。我可以通过使其 setter 受保护而不提供更新它的方法来强制执行此操作,也就是说,它必须通过构造函数创建,例如:
var myEntity = new MyEntity(myOtherEntity);
MyEntity 有这个属性:
public virtual MyOtherEntity Other { get; protected set; }
所以 EF 不会对此属性执行验证,但我可以确保它在构造函数中不为空。这是一种情况。
假设你不想以那种方式使用构造函数,你仍然可以使用自定义属性来确保验证,例如:
[RequiredForAdd]
public virtual MyOtherEntity Other { get; set; }
RequiredForAdd 属性是继承自notRequiredAttribute 属性的自定义属性。除了基本属性或方法之外,它没有任何属性或方法。
在我的 DB Context 类中,我有一个静态构造函数,它可以找到所有具有这些属性的属性:
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());
// Always use the default validator.
var result = base.ValidateEntity(entry, items);
// In our case, we only wanted to validate on Add and our known properties.
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;
}
请注意,我只对验证添加感兴趣;如果您还想在 Modify 期间进行检查,则需要对属性执行强制加载或使用 Sql 命令来检查外键值(不应该已经在上下文中的某处)?
由于Required 属性已被移除,EF 将创建一个可为空的FK;为了确保您的数据库完整性,您可以在创建数据库后在针对数据库运行的 Sql 脚本中手动更改 FK。这至少会捕获 Modify with null 问题。