【问题标题】:Function that can take different tables and operate on common columns可以取不同表并对常用列进行操作的函数
【发布时间】:2014-10-04 03:04:44
【问题描述】:

我有许多不同的表,每个表都有一些公共列和每个表独有的一些列。我正在使用实体框架对这些表进行操作,并使用 LINQ 查询进行过滤、排序等。下面的代码是我遇到的情况的高度简化示例。

        var filter = "A";
        using (var dc = new MyDataEntities())
        {
            var rpt1 = dc.Table1.Where(x => x.Name.StartsWith(filter));
            var rpt2 = dc.Table2.Where(x => x.Name.StartsWith(filter));
            var rpt3 = dc.Table3.Where(x => x.Name.StartsWith(filter));
        }

如果我决定将过滤器从 StartsWith 更改为 Contains,例如,我必须在 3 个位置进行更改。如果我决定要添加排序,我必须在 3 个地方添加它,等等。(你可以猜到我的实际代码在多个类中发生了这种逻辑,而且不止 3 个实例。)

有没有什么方法可以编写一个函数来获取任何表并对该表中的公共列进行操作?所以我的最终结果是这样的:

        using (var dc = new MyDataEntities())
        {
            var rpt1 = Filter(dc.Table1);
            var rpt2 = Filter(dc.Table2);
            var rpt3 = Filter(dc.Table3);
        }

这样,我可以将过滤任何表的 Name 列的逻辑放入一个 Filter() 函数中。想法?

【问题讨论】:

  • 您是否考虑过为您的实体添加接口,例如INamedEntity { string Name {get;set; }?然后您可以编写一个包含您的查询并具有通用查询方法的类,例如List<TEntity> GetByName<TEntity>(string name) where TEntity : class, INamedEntity。或返回IQueryable 进一步过滤。
  • rpt1, rpt2, rpt3都是同一个类型,和Table1, Table2, Table3一样吗?
  • @dbarnes 就目前而言,它们是不同的类型,即 rpt1 将是 IEnumerable,rpt2 是 IEnumerable 等。如果将它们汇集到一个通用类型中可以实现解决方案,那么值得考虑.
  • 如果你把常用的列抽象出来就容易多了,我可以用抽象的和不抽象的都给出答案,你可以自己打电话。

标签: c# linq entity-framework


【解决方案1】:

让我定义所有涉及的类:

public class Table1
{
    public string Name { get; set; }
}

public class Table2
{
    public string Name { get; set; }
}

public class Table3
{
    public string Name { get; set; }
}

正如你所知道的那样,POCO 会更复杂,但对于这个例子来说应该没问题:

public class Example
{
    public void Test()
    {
        var t1 = new List<Table1>();
        var t2 = new List<Table2>();
        var t3 = new List<Table3>();
        var filter = "hello";
        Func<string, bool> filterFunc = (x) => x.StartsWith(filter);

        var rpt1 = t1.Where(x => filterFunc(x.Name));
        var rpt2 = t2.Where(x => filterFunc(x.Name));
        var rpt3 = t3.Where(x => filterFunc(x.Name));

    }
}

如您所见,我已将过滤器抽象为 function delegate

现在一个可能更好的解决方案,取决于这是否真的有意义,是将所有共享列放入所有这些都派生自的基类:

public class TableCommon
{
    public string Name { get; set; }
}

public class Table1 : TableCommon
{}

public class Table2 : TableCommon
{}

public class Table3 : TableCommon
{}


public class Example
{

    public void Test2()
    {
        var t1 = new List<Table1>();
        var t2 = new List<Table2>();
        var t3 = new List<Table3>();

        var rpt1 = FilterData(t1);
        var rpt2 = FilterData(t2);
        var rpt3 = FilterData(t3);

    }

    public IEnumerable<T> FilterData<T>(IEnumerable<T> data) where T : TableCommon
    {
        var filter = "hello";
        Func<T, bool> pred = (x) => x.Name.StartsWith(filter);
        return data.Where(pred);
    }

}

这样做的好处是现在您可以隐藏您的过滤器逻辑,甚至可以通过将 pred 变量作为参数并允许该函数更加通用来传递不同的过滤器。

现在,如果您不习惯使用基类和 FilterData 上的类型约束的这种方式,那么您将不得不使用反射,因为其他原因我不得不这样做,它变得非常混乱且难以阅读快速地。那或者像dynamic linq 这样的东西可能又会非常混乱。

【讨论】:

  • 彻底的回应,非常感谢。我同意第二种解决方案会更好,但是深入研究 Entity Framework 为这些表生成的代码的内容比我现在想咬的要多。函数委托方法至少可以让我将逻辑抽象到一个地方。再次感谢。
猜你喜欢
  • 2019-05-07
  • 2015-07-20
  • 1970-01-01
  • 2016-11-19
  • 1970-01-01
  • 1970-01-01
  • 2011-11-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多