0

我的应用程序有一组实现 IDataItem 的类型。它们都是代表数据库行的简单“DTO”类型。通过对数据库运行 SQL 选择查询,使用 PetaPoco(一种微型 ORM)创建实例。SQL 选择查询特定于类型,并且还取决于应用程序版本号(假设类型 X 具有属性 Y,但在应用程序版本 N 之前,DB 中没有对应的列 - 在这种情况下,查询返回默认值对于 Y)。

该应用程序的性质是这些类型有很多,并且将来数量会增加。我正在尝试在数据访问层中使用泛型来 (i) 最大限度地减少代码量并 (ii) 迫使未来的开发人员在设计类型时设计必要的 SQL 查询。

因此,在数据访问层中,我想要的是一种方法:

  • 有一个类型参数 T:IDataItem
  • 返回 T 的 IEnumerable
  • 查找与 T 关联的 SQL 选择查询
  • 使用 PetaPoco 对数据库运行查询以创建 T 列表(它比这更复杂一些 - 还有一个动态 where 子句,但为了保持简单而将其省略)

我的挑战是 - 如何在创建 T 的任何实例之前检索类型参数 T 的 SQL 查询。理想情况下,我会向 IDataItem 添加一个方法,例如字符串 SqlSelectFrom(int appVersion),但是我需要一个实例来调用它(如果只有一个接口可能需要一个静态成员!)。我现在拥有的最好的是数据访问层中的一个函数,如下所示(但这并不能真正满足我强制未来类型的开发人员实现 SQL 查询的要求):

    private string GetSqlSelectFrom<T>(int appVersion) where T : IDataItem
    {
        if (typeof(T) == typeof(ProductDto))
            return "select ProductId, ProductCode from Product";

        if (typeof(T) == typeof(ColourDto))
            return "select ColourId, ColourCode from Colour";

        etc

        throw new Exception("No SQL defined!");
    }

有没有更好的模式来实现这一点?

4

1 回答 1

1

一种方法是为 DTO 类型使用一种注册表:

public static class DtoRegistry
{
    private static readonly Dictionary<Tuple<Type, int>, string> _sqlSelectByType =
        new Dictionary<Tuple<Type, int>, string>();

    public static void RegisterDto<T>(int appVersion, string sqlSelect) where T : IDataItem
    {
        var key = new Tuple<Type, int>(typeof(T), appVersion);
        _sqlSelectByType[key] = sqlSelect;
    }

    public static string GetSqlSelectFrom<T>(int appVersion) where T : IDataItem
    {
        var key = new Tuple<Type, int>(typeof(T), appVersion);
        return _sqlSelectByType[key];
    }
}

在应用程序启动代码的某处,必须注册所有 DTO 类型:

DtoRegistry.RegisterDto<ProductDto>(appVersion: 1, sqlSelect: "SELECT * FROM Products");
DtoRegistry.RegisterDto<ProductDto>(appVersion: 2, sqlSelect: "SELECT * FROM ProductsV2");

DtoRegistry.RegisterDto<ColourDto>(appVersion: 1, sqlSelect: "SELECT * FROM Colours");
DtoRegistry.RegisterDto<ColourDto>(appVersion: 2, sqlSelect: "SELECT * FROM ColoursV2");

注册表的一个警告是线程安全。我会让应用程序初始化在单线程中运行,填充注册表,并且在初始化完成后不允许更改注册表。然后在应用程序执行期间,多个线程可以安全地并发调用GetSqlSelectFrom。

于 2016-07-14T07:45:44.280 回答