实现 IDataItem 的每个类型都必须具有关联的字符串(SQL select 查询)但在类型实例化之前需要访问

Each type implementing IDataItem must have associated string (an SQL select qry) but access needed before type instantiated

我的应用程序有一组实现 IDataItem 的类型。它们都是表示数据库行的简单 "DTO" 类型。实例是使用 PetaPoco(一种微型 ORM)通过 运行 对数据库的 SQL select 查询创建的。 SQL select查询是特定于类型的,也取决于应用程序版本号(假设类型X有一个属性 Y,但在应用程序版本N之前没有对应的列数据库 - 在这种情况下,查询 returns Y 的默认值)。

应用程序的本质是有很多这样的类型,而且未来的数量还会增加。我正在尝试在数据访问层中使用泛型来 (i) 最小化代码量和 (ii) 迫使未来的开发人员在设计类型时设计必要的 SQL 查询。

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

我的挑战是 - 如何在创建 T 的任何实例之前检索类型参数 T 的 SQL 查询。理想情况下,我会向 IDataItem 添加一个方法,例如 string 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!");
    }

有没有更好的模式来完成这个?

一种方法是对 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。