在LINQ to Entities(EF4.1)中返回具有类型约束的泛型的输入类型

Jas*_*rez 10 c# linq generics linq-to-entities

我有一个简单的扩展方法,用于按标签过滤LINQ IQueryable.我正在使用LINQ to Entities,接口为:

public interface ITaggable
{
    ICollection<Tag> Tags { get; } 
}
Run Code Online (Sandbox Code Playgroud)

以下不起作用,返回IQueryable<ITaggable>而不是IQueryable<T>:

public static IQueryable<T> WhereTagged<T>(this IQueryable<T> set, string tag) where T:ITaggable
    {
        return set.Where(s=>s.Tags.Any(t=>t.Name.ToLower() == tag.ToLower()));
    }
Run Code Online (Sandbox Code Playgroud)

这导致LINQ to Entities强制转换异常:

"无法将类型'ReleaseGateway.Models.Product'转换为'ReleaseGateway.Models.ITaggable'.LINQ to Entities仅支持转换实体数据模型基元类型." (System.NotSupportedException)捕获到System.NotSupportedException:"无法将类型'Project.Models.Product'强制转换为'Project.Models.ITaggable'.LINQ to Entities仅支持转换实体数据模型基元类型."

它没有像这样的约束,但我必须在我的应用程序代码中显式声明类型T:

public static IQueryable<T> WhereTagged<T>(this IQueryable<ITaggable> set, string tag)
{
    return set.Where(s=>s.Tags.Any(t=>t.Name.ToLower() == tag.ToLower())).Cast<T>();
}
Run Code Online (Sandbox Code Playgroud)

问题:为什么类型约束会转换返回类型?我可以重写这个以利用扩展方法调用者推断类型吗?

Str*_*ior 7

我怀疑这个问题来自于呼吁s.Tags.因为s是a Product,但是你正在调用ITaggable.Tags,生成的表达式看起来更像:

set.Where(s=>((ITaggable)s).Tags.Any(...))
Run Code Online (Sandbox Code Playgroud)

这只会混淆实体框架.试试这个:

((IQueryable<ITaggable>)set)
    .Where(s=>s.Tags.Any(t=>t.Name.ToLower() == tag.ToLower()))
    .Cast<T>();
Run Code Online (Sandbox Code Playgroud)

由于IQueryable是一个协变接口,这将把集合视为一个IQueryable<ITaggable>,它应该工作,因为你的第二个例子基本上完全相同的事情.


Max*_*ber 2

我一直在寻找相同的答案,并且对所提供的答案的语法简洁性不满意,我一直在寻找并找到了这篇文章。

太长;博士;-将类添加到您的约束中并且它可以工作

LINQ to Entities 仅支持使用 IEntity 接口转换 EDM 基元或枚举类型

public static IQueryable<T> WhereTagged<T>(this IQueryable<T> set, string tag)
    where T: class, ITaggable
Run Code Online (Sandbox Code Playgroud)