【问题标题】:Need help simplifying methods using generics in C#需要帮助在 C# 中使用泛型简化方法
【发布时间】:2016-04-28 21:30:59
【问题描述】:

我有下面这样的函数,它使用 LinqToSql 加载数据库表并返回转换为我的自定义模型类的条目列表:

    public List<AreaModel> LoadListOfAreaModels(Boolean includeDeleted = false)
    {
        using (LinqToSqlDataContext dc = new LinqToSqlDataContext())
        {
            IQueryable<Areas> filtered = includeDeleted
                ? dc.Areas.Where((c) => !c.DataRowDeleted)
                : dc.Areas;
            return filtered.Select((c) => AreaModel.ModelFactoryFromLinq(c)).ToList();
        }
    }

现在我有很多表,都必须以相同的方式加载和转换。

我是否可以避免多次键入此函数而只需将 AreaModelAreas 更改为例如TownModelTowns 使用泛型函数?

我已经尝试过了,但失败了,因为如果我只有泛型类型参数,我找不到调用静态类方法的方法。

更多信息:所有XxxModel 类都有公共超类ModelBase(它不包含我需要调用的静态工厂方法!),所有Linq 表类(如AreasTowns)都实现通用接口ILinqClass

关于如何以最优雅的方式实现这一点的任何想法?


knittl 的回答启发,我想出了这个解决方案:

    public List<TModel> LoadListOfModels<TLinq, TModel>(
        Func<TLinq, bool> filter,
        Func<TLinq, TModel> modelFactory
        )
        where TLinq : class, ILinqClass
        where TModel : ModelBase
    {
        using (LinqToSqlDataContext dc = new LinqToSqlDataContext())
        {
            return dc.GetTable<TLinq>()
                .Where(filter)
                .Select(modelFactory)
                .ToList();
        }
    }

【问题讨论】:

  • DataRowDeleted 是否在 ModelBase 中定义?
  • @Lee 是的,DataRowDeleted 它是接口的一部分 ModelBase 实现
  • 您提到的问题是 AreaModel 是一个静态类。你为什么不试着让你的 AreaModel、TownModel 成为一个非静态类呢?然后他们可以实现一个接口,比如带有方法 ModelFactoryFromLinq 的 IModelFactory ,然后您可以将此接口的实现传递给您的 LoadList... 方法。
  • @JordiRuiz AreaModel 不是静态的,由于参数和返回类型不同,我无法将 ModelFactoryFromLinq() 放入接口。

标签: c# generics


【解决方案1】:

你应该能够传递委托/函数:

public List<TModel> LoadListOfModels<TDbModel, TModel>(
  Func<LinqToSqlDataContext, TDbModel> modelSelector,
  Func<TDbModel, bool> isDeleted,
  Func<TDbModel, TModel> modelFactory,
  bool includeDeleted = false)
{
    using (LinqToSqlDataContext dc = new LinqToSqlDataContext())
    {
        var models = modelSelector(dc);
        var filtered = includeDeleted
            ? models.Where(c => !isDeleted(c))
            : models;

        return filtered.Select(modelFactory).ToList();
    }
}

对于Area 调用如下:

 LoadListOfModels(
   dc => dc.Areas,
   c => c.DataRowDeleted,
   Area.ModelFactoryFromLinq,
   includeDeleted);

对于Town

 LoadListOfModels(
   dc => dc.Towns,
   c => c.DataRowDeleted,
   Town.ModelFactoryFromLinq,
   includeDeleted);

您甚至可以去掉includeDeleted 参数。只需传递一个过滤器函数Func&lt;TDbModel, bool&gt; filter

return modelsSelector(dc)
  .Where(filter)
  .Select(modelFactory)
  .ToList();

呼叫:

LoadListOfModels(dc => dc.Areas, c => true, Area.ModelFactoryFromLinq);
LoadListOfModels(dc => dc.Areas, c => !c.DataRowDeleted, Area.ModelFactoryFromLinq);

【讨论】:

  • 谢谢,我也设法摆脱了 modelSelector 参数,请参阅我编辑的问题以了解我的最终方法。
【解决方案2】:

试试这个方法:

public abstract class BaseClass<TModel> where TModel : class
{
    public bool DataRowDeleted { get; set; }
    public abstract TModel ModelFactoryFromLinq();
}

public class Area : BaseClass<AreaModel>
{
    public override AreaModel ModelFactoryFromLinq()
    {
        return new AreaModel();
    }
}

public static List<TModel> LoadListOfAreaModels<TContext, TModel>(bool includeDeleted = false)
    where TContext : BaseClass<TModel>, new()
    where TModel : class
{
    using (var dc = new LinqToSqlDataContext())
    {
        IQueryable<TContext> filtered = includeDeleted
            ? dc.GetTable<TContext>().Where((c) => !c.DataRowDeleted)
            : dc.GetTable<TContext>();
        return filtered.Select((c) => c.ModelFactoryFromLinq()).ToList();
    }
}

实施:

List<AreaModel> result = SomeClassName.LoadListOfAreaModels<Area, AreaModel>();

【讨论】:

    【解决方案3】:

    如果在ModelBase 中定义了DataRowDeleted,您应该可以这样做:

    public static IQueryable<TModel> LoadListOfQuery<T, TModel>(IQueryable<T> source, Expression<Func<T, TModel>> selector, bool includeDeleted = false) where T : ModelBase {
        IQueryable<T> filtered = includeDeleted ? source : source.Where(s => !s.DataRowDeleted);
        return filtered.Select(selector);
    }
    

    然后添加一个函数来评估给定上下文的查询:

    public static List<T> ExecList(Func<LinqToSqlDataContext, IQueryable<T>> qf)
    {
        using(var c = new LinqToSqlDataContext())
        {
             var q = qf(c);
             return q.ToList();
        }
    }
    

    那么你可以这样做:

    public List<AreaModel> LoadListOfAreaModels(bool includeDeleted = false)
    {
        ExecList(c => LoadListOfQuery(c.Areas, a => AreaModel.ModelFactoryFromLinq(a), includeDeleted);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-13
      • 1970-01-01
      • 1970-01-01
      • 2011-08-13
      • 1970-01-01
      • 1970-01-01
      • 2013-07-11
      • 2014-01-10
      相关资源
      最近更新 更多