检查附加对象是否具有导航属性上的任何相关数据库

问题描述:

我试图创建一个函数,该函数可以检测一个附加对象是否在不同表中具有相关数据。检查附加对象是否具有导航属性上的任何相关数据库

我希望避免级联删除,而是警告用户手动删除孩子。 它必须是动态的,每个导航属性也是未知类型。 有太多的各种类实例,属性每天都在改变,所以我不能硬编码,否则我只能一个一个地计算它们。

我的问题是,当我通过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); 
} 
+1

非常先进,对EF理解深刻,我真的会学到很多你的代码,并且你节省了我的一天(敬礼)。 我正在考虑的一件事是,它必须约束的对象类型必须从Type参数派生? 如果是的话,是否有我们可以在签名中做这样的约束?或者我们应该在逻辑中验证它?经过测试并仍在学习,会在有更多问题时回来,感谢您的帮助! – Vincent

+0

btw,你会介意告诉我关于.GetMethod(“get_Count”)吗?你怎么弄出字符串“get_Count”,因为我只知道你在调用.Count(),但是这个模式是如何工作的?我猜可能像“accessor_FnName”? – Vincent

+0

很好的考虑,我更新了帖子以更改方法签名。关于'.GetMethod(“get_Count”)'我想通过'property.PropertyType.GetMethods()'寻找反射方法信息。我不知道为什么有些方法有这种模式,这是一个很好的问题!礼炮! –