Linq to Entities和复杂类型转换

时间:2016-09-21 13:48:04

标签: c# entity-framework linq casting

好的,我在这里有很多关于这种性质的问题,但没有一个能够完全或正确地解决这个问题。

假设我有以下代码......

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();
}

在该方法中我想添加一个检查,可能会导致我动态地将一个简单的where子句添加到我的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)));

 }

... 它本身就是一个简单的where子句,如果我知道T在设计时是什么,那么这将是一个非问题。

然而,将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

编辑:一些经过测试的实施......

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

1 个答案:

答案 0 :(得分: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(当然还有一点开销)。