2016-09-21 4 views
2

Хорошо, у меня есть тонна вопросов, заданных здесь, но ни одна из них не решит эту проблему полностью или правильно.Linq to Entities и литье сложного типа

Давайте предположим, что у меня есть следующий код ...

public interface IHaveRoles { 
    ICollection<Role> Roles { get;set; } 
} 

public class Foo : IHaveRoles { 
    public ICollection<Role> Roles { get;set; } 
} 

public class Bar { } 

... то у меня есть метод, как это ...

public override IQueryable<T> GetAll() 
{ 
    return base.GetAll(); 
} 

в этому методу я хочу добавить проверку что может привести меня динамически добавляя простой, где положение моей IQueryable ...

if(typeof(IHaveRoles<Role>).IsAssignableFrom(typeof(T))) { 
     return base.GetAll() 
      .Where(i => i.Roles.Any(r => r.Users.Any(u => u.Id == User.Id))); 

} 

... Это достаточно просто, где предложение в его собственном праве, и если бы я знал, что Т был во время разработки, это было бы проблемой.

Однако, отбрасывая result к IQueryable<IHaveRoles> не вариант, потому что, когда я сделал добавление моего положения, я больше не могу бросить его обратно к IQueryable<T> как IHaveRoles не является подтип T

Как сделать мы решим эту проблему, в то время сохраняя способность возвращать IQueryable<T> и без незаконных слепков, как показано в некоторых из ответов на другие вопросы, как это ...

Cast Entity to Implemented Interface in a Generic Method Using LINQ for Entity Framework

LINQ-to-entities casting issue

... и таким образом, чтобы избежать проблем с EF не поддерживает не EDM примитивных типов ...

LINQ to Entities only supports casting EDM primitive or enumeration types with IEntity interface

EDIT: Некоторые испытанные реализации ...

public override IQueryable<T> GetAll() 
{ 
    var result = base.GetAll(); 

    if (typeof(IHaveRoles).IsAssignableFrom(typeof(T)) && !AuthInfo.SignatureIsValid) 
    { 
     // tried implementations that don't work ... 

     // InvalidCastException (CLR can't cast an IQueryable<IHaveRoles> to a IQueryable<T> 
     var queryableRoleSecured = ((IQueryable<IHaveRoles>)result); 
     result = (IQueryable<T>)queryableRoleSecured 
      .Where(i => i.Roles.Any(r => User.Roles.Contains(r))); 

     // NotSupportedException (EF won't accept this kind of casting) 
     result = result 
      .Where(i => ((IHaveRoles)i).Roles.Any(r => r.Users.Any(u => u.Id == User.Id))); 
    } 

    return result; 
} 

ответ

2

Ничего себе, я никогда не понимаю, что динамический linq, похоже, не имеет границ, но здесь мы снова.

Вот что я придумал ...

public override IQueryable<T> GetAll() 
{ 
    var result = base.GetAll(); 

    if (typeof(IHaveRoles).IsAssignableFrom(typeof(T)) && !AuthInfo.SignatureIsValid) 
     result = result.Where("Roles.Any(Users.Any(Id == @0))", User.Id); 

    return result; 
} 

, поскольку динамика оценивается во время выполнения то же время дизайна правило не применяется, то есть я могу применить то же LINQ я хотел (с небольшим бит накладных расходов, конечно).