2017-09-13 3 views
2

J'ai essayé de créer une fonction qui pourrait détecter si un objet attaché possède des données connexes dans une table différente.Vérifier si un objet attaché possède une base de données associée sur la propriété de navigation

Je m'attends à éviter la suppression en cascade mais je préviens l'utilisateur de retirer manuellement les enfants à la place. Il doit être dynamique et chaque propriété de navigation est également de type inconnu. Il y a trop d'instances de classe différentes, et les propriétés changent tous les jours, donc je ne peux pas coder dur sinon je peux les compter un par un. Mon problème est que lorsque je sélectionne la valeur return par Property.GetValue(), il s'agit d'un objet encadré et il y a aussi une collection de types dynamiques, donc je ne peux pas compter l'enregistrement et faire la vérification correspondante.

Ma question est comment puis-je convertir l'objet en ICollection se référer au type dynamique dans la méthode Linq?

Je passais une journée entière et je n'ai pas eu de réponse, c'est peut-être mon incompréhension du concept EF, mais merci de nous aider, merci beaucoup!

//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; 
} 

Répondre

1

J'ai créé une méthode de base pour itérer les propriétés de navigation d'une entité 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; 
} 

Vous devez utiliser les références ci-dessous:

using System.Data.Entity; 
using System.Data.Entity.Infrastructure; 
using System.Linq; 

Il faut donc utiliser:

using (var ctx = new Context()) 
{ 
    var course = ctx.Course.Find(2); 
    var cannotDel = CheckIfAnyNavigationHasData(course, ctx); 
} 
+1

Très avance et la profondeur de la compréhension EF , J'apprendrais vraiment beaucoup de ton code, et tu sauveras ma journée (salut). une chose que je considère est-il nécessaire de contraindre le type d'objet doit être dérivé du paramètre Type? Si oui, y at-il de toute façon nous pouvons faire une telle contrainte dans la signature ?? ou nous devrions le valider dans la logique? Testé et encore étudiant, reviendra quand il y a plus de questions, appréciez votre aide! – Vincent

+0

btw, cela vous dérangerait-il de me parler du .GetMethod ("get_Count")? comment trouvez-vous la chaîne "get_Count" puisque je sais seulement que vous appelez le .Count() mais comment ce modèle fonctionne? Je suppose que peut-être comme "accessor_FnName" ?? – Vincent

+0

Une grande considération, j'ai mis à jour le poste pour changer la signature de la méthode. À propos de '.GetMethod (" get_Count ")' J'ai trouvé la méthode de réflexion avec 'property.PropertyType.GetMethods()'. Je ne sais pas pourquoi certaines méthodes ont ce modèle, c'est une bonne question! saluer! –