检查附加对象是否具有导航属性上的任何相关数据库
问题描述:
我试图创建一个函数,该函数可以检测一个附加对象是否在不同表中具有相关数据。检查附加对象是否具有导航属性上的任何相关数据库
我希望避免级联删除,而是警告用户手动删除孩子。 它必须是动态的,每个导航属性也是未知类型。 有太多的各种类实例,属性每天都在改变,所以我不能硬编码,否则我只能一个一个地计算它们。
我的问题是,当我通过Property.GetValue()选择值返回时,它是盒装对象,并且里面还有动态类型集合,因此我无法计数记录并执行相关检查。
我的问题是如何将对象转换为ICollection引用Linq方法中的动态类型?
我花了一整天的时间,无法得到答案,也许这是我对EF概念的误解,但请大家帮忙,非常感谢!
//Domain Model
public class Course
{
[Key]
public int CourseID { get; set; }
[ForeignKey("CourseID")]
public virtual ICollection<CourseTeacher> Teachers { get; set; }
[ForeignKey("CourseID")]
public virtual ICollection<CourseInfo> CourseInfo { get; set; }
[ForeignKey("CourseID")]
public virtual ICollection<CourseSession> Sessions { get; set; }
}
// Controller Delete Action
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Course course = db.Courses.Find(id);
bool CannotDel = ValidateRelationalData(typeof(course), course);
//if failed, warn the user and stop the delete action otherwise delete this record
}
// Public function I was trying to make
public static bool ValidateRelationalData(Type anyType, dynamic anyInstance)
{
bool IsExisted = anyType.GetProperties()
.Where(p => typeof(IEnumerable).IsAssignableFrom(p.PropertyType) &&
p.PropertyType != typeof(byte[]) &&
p.PropertyType != typeof(string)
)
.Select(prop => (ICollection)prop.GetValue(anyInstance, null))
.Where(c => c.Count() > 0)
.Any(); //(ICollection)prop.GetValue(anyInstance, null)) won't work here, because no specific type
return IsExisted;
}
答
我创建了一个基础的方法来遍历实体的导航属性DbSet:
private static bool CheckIfAnyNavigationHasData<T>(T o, DbContext context) where T : class
{
var objectContext = ((IObjectContextAdapter)context).ObjectContext;
var elementType = objectContext.CreateObjectSet<T>().EntitySet.ElementType;
var navigations = elementType.DeclaredNavigationProperties;
var collectionNavigations = typeof(T).GetProperties().Where(w => w.PropertyType.Name.Equals(typeof(ICollection<>).Name)
|| w.PropertyType.Name.Equals(typeof(HashSet<>).Name)
|| w.PropertyType.Name.Equals(typeof(IList<>).Name))
.Join(navigations, t => t.Name, n => n.Name, (t, n) => t).ToArray();
foreach (var property in collectionNavigations)
{
var p = o.GetType().GetProperty(property.Name);
if (p == null)
continue;
var propertyValue = p.GetValue(o);
if (propertyValue == null)
continue;
if ((int)property.PropertyType.GetMethod("get_Count").Invoke(propertyValue, null) > 0)
return true;
}
return false;
}
您需要使用下面的参考资料:
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
所以使用:
using (var ctx = new Context())
{
var course = ctx.Course.Find(2);
var cannotDel = CheckIfAnyNavigationHasData(course, ctx);
}
非常先进,对EF理解深刻,我真的会学到很多你的代码,并且你节省了我的一天(敬礼)。 我正在考虑的一件事是,它必须约束的对象类型必须从Type参数派生? 如果是的话,是否有我们可以在签名中做这样的约束?或者我们应该在逻辑中验证它?经过测试并仍在学习,会在有更多问题时回来,感谢您的帮助! – Vincent
btw,你会介意告诉我关于.GetMethod(“get_Count”)吗?你怎么弄出字符串“get_Count”,因为我只知道你在调用.Count(),但是这个模式是如何工作的?我猜可能像“accessor_FnName”? – Vincent
很好的考虑,我更新了帖子以更改方法签名。关于'.GetMethod(“get_Count”)'我想通过'property.PropertyType.GetMethods()'寻找反射方法信息。我不知道为什么有些方法有这种模式,这是一个很好的问题!礼炮! –