【问题标题】:LINQ : Generics with IQueryableLINQ:具有 IQueryable 的泛型
【发布时间】:2023-03-02 23:57:02
【问题描述】:

我必须向我的 C#.NET 应用程序添加一些“查找”查询。基本上,会有很多表都具有相同的模式,但包含不同的值。

我面临一遍又一遍地编写相同的代码(应该是一种 OO 方式来做到这一点..)

[编辑 - 下面被修改以显示更完整的信息] 无论如何,我想做的是:

public List<GenericLookupE>         GetLookupItems( string what )
{
    // create db thing
    if ( "regions" == what )  return FetchLookup( db.lkRegions.AsQueryable() );
    if ( "partners" == what ) return FetchLookup( db.lkPartners.AsQueryable() );
    if ( "funders" == what )  return FetchLookup( db.lkFunders.AsQueryable() );

    return null; // or something more intelligent than that =)
}


private List<GenericLookupE>        FetchLookup<T>( IQueryable<T> lookup )
{
    return lookup.OrderBy( p => p.Sequence ).Select
    (   p => new GenericLookupE()
        {
            ID      = p.ID
            ,Label      = p.Label
            ,StateCode  = p.StateCode
            ,Sequence   = p.Sequence
        }
    ).ToList();
}

当然,问题是编译器不知道'p => p.Sequence' 是什么。 有什么想法吗?

谢谢大家。

【问题讨论】:

  • 这里没有人知道p =&gt; p.Sequence是什么...你能详细说明一下吗?
  • 我刚刚得出了同样的结论。 -- 更新问题

标签: .net linq generics


【解决方案1】:

泛型的类型约束是你的朋友。使用包含序列的基类型或接口:

where T : IMyBaseInterface

http://msdn.microsoft.com/en-us/library/bb384067.aspx

【讨论】:

    【解决方案2】:

    为您的Т 类添加一个通用接口,在那里定义Sequence,并为您的泛型添加一个约束,要求Т 参数来实现通用接口。

    interface ICommonInterface { // This is an awful name, please pick something better
        int Sequence { get }
    }
    
    private List<GenericLookupE> FetchLookup<T>( IQueryable<T> lookup ) where T : ICommonInterface {
        // Your stuff here
    }
    

    【讨论】:

    • sitbacken watch: 嗯..我理解你的想法,但问题不在于编译器不理解 GenericLookupE,而是表中的字段(p.Sequence 和其他),以及窒息那个。 (或者也许我不明白你的想法.. =)
    • @horace 抱歉,我输入的是 GenericLookupE 而不是 T
    【解决方案3】:

    干净的解决方案是使用通用的基本类型或通用接口。

    如果由于某种原因这是不可能的,这里有一种动态构造你想要的表达式的方法:

    // Returns an expression of the form: p => new GenericLookupE
    // { ID = p.ID, Label = p.Label, StateCode = p.StateCode, Sequence = p.Sequence }
    // Can be written as a more generic 'auto-mapper', but
    // this sample only solves your specific problem.
    static Expression<Func<T, GenericLookupE>> GetLookupMapper<T>()
    { 
        var parameter = Expression.Parameter(typeof(T), "p");
    
        string[] properties = { "ID", "Label", "StateCode", "Sequence" };
    
        var bindings = from propName in properties
                       let source = Expression.Property(parameter, propName)
                       let target = typeof(GenericLookupE).GetProperty(propName)
                       select Expression.Bind(target, source);    
    
        var newExp = Expression.New(typeof(GenericLookupE));    
        var body = Expression.MemberInit(newExp, bindings);
    
        return Expression.Lambda<Func<T, GenericLookupE>>(body, parameter);
    }
    

    然后你的方法就变成了:

    private List<GenericLookupE> FetchLookup<T>(IQueryable<T> lookup)
    {
        return lookup.Select(GetLookupMapper<T>())
                     .OrderBy(p => p.Sequence)
                     .ToList();
    }
    

    我真的怀疑你是否需要这样的东西。

    【讨论】:

    • 实际上,这看起来是唯一不需要一遍又一遍地重复相同代码的解决方案。即使我使用部分类解决方案,我也会一遍又一遍地为我想要查询的每个表编写几乎相同的部分类。 ...至少您的解决方案无需一遍又一遍地编写几乎重复的代码。
    【解决方案4】:

    您的对象RegionsPartnersFunders 似乎具有相同的一组属性(IDLabelStateCodeSequence)。这通常称为“鸭子打字”。

    但是,要编写一个适用于所有这些的“通用”强类型解决方案,它们都必须从列出这些属性的通用接口或基类型派生!

    我看到以下 3 个解决方案:

    1. 使RegionsPartnersFunders继承一个基类型(如GenericLookupE),或使它们实现一个通用接口(如IGenericLookupE)。
      • 如果这些类是为您生成的,那么可能很难做到这一点
      • 如果您能做到这一点,那么您只需 .Cast 到正确的类型,而无需 Selecting 新对象(查看更新
    2. 使用dynamic 类型或反射来获取属性(类型松散,但可以工作)
    3. 为每种类型创建一次性解决方案(这基本上是您要避免的)

    更新

    以下是一些使您的数据库对象实现通用接口的方法,具体取决于您的框架:

    Adding a Base Class (or Interface) to All LINQ to SQL Entities

    Implementing an interface on a LINQ to SQL object using a partial class

    【讨论】:

    • 嗨,斯科特。这些对象是通过从服务器资源管理器中拖动表自动创建的数据库表对象。我可以控制他们继承的内容吗?
    • @horace 假设您使用的是 LINQ to SQL,我用一些链接更新了我的答案。
    • 嗨,scott:好吧,看起来解决方案 3(1-off)和其他解决方案一样实用。如果我必须为每种类型编写一个部分类,那么我实际上是在编写更多代码,而不是一遍又一遍地重复相同的代码。如果我添加一个属性或更改一个属性的类型,我将面临更多使用“oo”方式的工作,而不仅仅是复制/粘贴。
    • @horace 是的,我同意,这可能不值得。向所有实体添加基类怎么样?看起来 ti 会起作用吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-27
    • 1970-01-01
    • 1970-01-01
    • 2012-08-21
    • 1970-01-01
    相关资源
    最近更新 更多