Return input type of generic with type constraint in LINQ to Entities (EF4.1)

前端 未结 4 1286
春和景丽
春和景丽 2021-02-20 18:03

I have a simple extension method for filtering a LINQ IQueryable by tags. I\'m using this with LINQ to Entities with an interface of:

public interface ITaggable
         


        
相关标签:
4条回答
  • 2021-02-20 18:08

    I was looking for the same answer and not being satisfied with the syntactic cleanliness of the answers provided, I kept looking and found this post.

    tl;dr; - add class to your constraints and it works.

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

    public static IQueryable<T> WhereTagged<T>(this IQueryable<T> set, string tag)
        where T: class, ITaggable
    
    0 讨论(0)
  • 2021-02-20 18:16

    You don't need that Cast at the end as dlev mentioned.

    I assume the product class implements ITaggable? I think removing the Cast will fix the problem.

    0 讨论(0)
  • 2021-02-20 18:22

    I suspect that the problem arises from the call to s.Tags. Because s is a Product, but you're calling ITaggable.Tags, the expression that gets generated looks more like:

    set.Where(s=>((ITaggable)s).Tags.Any(...))
    

    That just confuses Entity Framework. Try this:

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

    Since IQueryable is a covariant interface, this will treat the set as an IQueryable<ITaggable>, which should work since your second example basically does exactly the same thing.

    0 讨论(0)
  • 2021-02-20 18:22

    You never show where this is used. I think you are already passing an IQueryable<ITaggable> to the method in the first place.

    Proof of concept https://ideone.com/W8c66

    using System;
    using System.Linq;
    using System.Collections.Generic;
    
    public class Program
    {
        public interface ITaggable {}
    
        public struct TagStruct : ITaggable {}
        public class  TagObject : ITaggable {}
    
        public static IEnumerable<T> DoSomething<T>(IEnumerable<T> input) 
            where T: ITaggable
        {
            foreach (var i in input) yield return i;
        }
    
        public static void Main(string[] args)
        {
            var structs = new [] { new TagStruct() };
            var objects = new [] { new TagObject() };
    
            Console.WriteLine(DoSomething(structs).First().GetType());
            Console.WriteLine(DoSomething(objects).First().GetType());               
        }
    }
    

    Output

    Program+TagStruct
    Program+TagObject
    

    So, it is returning the input type, not the constrained interface.

    Not surprising, what would have been the result if DoSometing required two interfaces?

        public static IEnumerable<T> DoSomething<T>(IEnumerable<T> input) 
            where T: ITaggable, ISerializable
    

    ??

    0 讨论(0)
提交回复
热议问题