C#泛型问题

时间:2009-09-12 02:58:43

标签: c# .net linq-to-sql generics reflection

我正在构建的应用程序中有几个区域,看起来我可能不得不违反DRY(不要重复自己)原则中的生活日光。我真的很想保持干爽而不会被人嘲笑,并且想知道是否有人可以给我一个雨披。作为背景,我使用的是C#/ .NET 3.51 SP1,Sql Server 2008和Linq-to-Sql。

基本上,我的情况围绕以下情况展开。我需要能够从我的数据库中的几乎任何表中检索过滤的项目列表,或者我需要能够从给定主键ID的数据库中的任何表中检索单个项目。我很确定这些问题的最佳解决方案将涉及大量的仿制药和/或反射。

以下是更深入的两个挑战。 (请原谅详细程度。)

  1. 给定一个表名(或者可能是一个复数表名),我希望能够检索表中已过滤的元素列表。具体而言,此功能将与查找表一起使用。 (此数据库中大约有50个查找表。经常添加和/或删除其他表。)当前查找表都实现了一个名为IReferenceData的接口(我的),并具有ID(PK),标题,描述和活跃。
  2. 对于每个查找表,我有时需要返回所有记录的列表。其他时候我只需要返回活动记录。任何Linq-to-Sql数据上下文自动包含每个TableName的List属性。不幸的是,我不相信我可以使用它的原始形式,因为它是未经过滤的,我需要在IsActive属性上应用过滤器。

    一种选择是为所有50个表编写类似于以下的代码。育!!!

    public List<AAA> GetListAAA(bool activeOnly)
    {
        return AAAs.Where(b => b.IsActive == true || b.IsActive == activeOnly).OrderBy(c => c.Title).ToList();
    }
    

    这不会非常困难,但它确实增加了维护的负担。

    注意:在返回列表时,保持基础数据类型非常重要。可以修改这些查找表中的记录,我必须适当地应用更新。

    1. 对于我的150个表中的每一个,我需要能够通过其主键id检索单个记录(FirstOrDefault或SingleOrDefault)。同样,我不想多次编写相同的代码。我希望有一种方法可以用于我的所有表格。
    2. 我不确定这里最好的方法是什么。我想到的一些可能性包括以下内容。 (我对其实施没有具体的想法。我只是将它们列为思考的食物。)

      一个。在数据上下文中有一个类似GetTableNameItemByID(Guid id)的方法。 (好) B.在数据上下文中有一个像GetItem(this,string tableName,Guid id)这样的扩展方法。 (更好) C.有一个像GetItem这样的通用方法或扩展方法(this,Table,Guid id)。 (我甚至不知道这是否可能,但它是最干净的。)(最佳)

      附加说明

      由于各种原因,我已经为我的数据上下文创建了一个部分类。如果方法作为普通方法或扩展方法的单独静态类包含在该部分类中,那肯定是可以接受的。

5 个答案:

答案 0 :(得分:6)

由于您已经部分实施了数据上下文,因此可以添加:

public IQueryable<T> GetList<T>( bool activeOnly ) where T : class, IReferenceData
{
     return this.GetTable<T>()
                .Where( b => !activeOnly || b.isActive )
                .OrderBy( c => c.Title );
}

保留数据的IQueryable字符将推迟执行查询,直到您准备好实现它为止。请注意,您可能希望省略默认顺序,或者使用单独的方法,无论是否订购,都可以根据需要应用不同的顺序。如果将它保留为IQueryable,这可能更有价值,因为如果您愿意,可以将它与分页一起使用以减少实际返回的数据量(每个查询)。

答案 1 :(得分:3)

有一种设计模式可以满足您的需求,称为“通用存储库”。使用此模式,您将获得IQueryable,而不是实体的实体列表,这样您就可以随时使用查询执行其他操作。点是让业务层在通用方法中随时获得所需的任何内容。

您可以找到示例here

答案 2 :(得分:2)

您是否考虑过使用代码生成工具?看看CodeSmith。使用这样的工具或T4将允许您自动生成过滤器功能,并使它们易于维护。

我不确定提供T4的最佳链接,但您可以从this video开始。

答案 3 :(得分:2)

这会满足您的需求吗?

public static IEnumerable<T> GetList<T>(this IEnumerable<IReferenceData> items, bool activeOnly)
{
    return items.Where(b => b.IsActive == true || b.IsActive == activeOnly).OrderBy(c => c.Title).Cast<T>().ToList();
}

您可以像这样使用它:

IEnumerable<IReferenceData> yourList;
List<DerivedClass> filtered = yourList.GetList<DerivedClass>(true);

答案 4 :(得分:1)

要做这样的事情而不需要接口等,你可以使用动态Expression;类似的东西:

public static IList<T> GetList<T>(
    this DataContext context, bool activeOnly )
    where T : class
{
    IQueryable<T> query = context.GetTable<T>();
    var param = Expression.Parameter(typeof(T), "row");
    if(activeOnly)
    {
        var predicate = Expression.Lambda<Func<T, bool>>(
            Expression.Equal(
                Expression.PropertyOrField(param, "IsActive"),
                Expression.Constant(true,typeof(bool))
            ), param);
        query = query.Where(predicate);
    }
    var selector = Expression.Lambda<Func<T, string>>(
        Expression.PropertyOrField(param, "Title"), param);
    return query.OrderBy(selector).ToList();
}