在对表、布局和记录进行建模时,如何避免代码重复,所有这些都共享相同的基本结构

本文关键字:所有这些 共享 结构 代码 布局 记录 建模 何避免 | 更新日期: 2023-09-27 18:24:07

这将是一个有点抽象的问题。

我正在研究一个数据访问层框架,该框架需要区分表、其抽象模式/布局和具体的表记录。我担心,由于这种区别,会有很多代码重复。我可能需要一些关于如何避免这种情况的意见。

+-----------+
|    Foo    |
+-----------+
| +Id: Guid |
+-----------+

请注意,该图可以描述其中的任何一个:表模式、具体表或具体表记录,具有类型为Guid的字段Id

  • 模式中已知的只是字段的名称和类型
  • 在具体的(打开的)表中,字段的"列索引"是额外已知的
  • 有了记录,所有这些事情都是已知的,加上字段有了具体的价值

把它翻译成代码,我会得到很多类似的类型(三对一)。我将使用接口来保持示例的简洁性;我想展示的是类型的相似性:

// these interfaces only need to be implemented once:
interface ISchemaField<T>  {  string Name  { get; }       }

interface ITableField<T>   {  string Name  { get; }        
                              int    Index { get; }       }
interface IRecordField<T>  {  string Name  { get; }        
                              int    Index { get; }       
                              T      Value { get; set; }  }

// these three interfaces are an example for one entity; there would be
// three additional types for each additional entity.
interface IFooSchema
{
    ISchemaField<Guid> Id { get; }
    IFooTable Open(IDbConnection dbConnection, string tableName);
}
interface IFooTable
{
    ITableField<Guid> Id { get; }
    ICollection<IFooRecord> ExecuteSomeQuery();
}
interface IFooRecord
{
    IRecordField<Guid> Id { get; }
}

现在,我想避免为具体数据模型中的每个实体编写三个非常相似的实现。减少代码重复的一些可能方法是什么?

  • 我曾想过代码生成(例如T4),这将是一个不错的解决方案,但我更喜欢"手动"编码的解决方案(如果有),代码行更少;更可读的代码。

  • 我考虑过为每个实体创建一个类,同时实现模式、表和记录接口。。。但这感觉很混乱,就像违反了"关注分离"。

在对表、布局和记录进行建模时,如何避免代码重复,所有这些都共享相同的基本结构

IMHO,我认为您对表结构的抽象做得有点过分了。过度使用接口会使代码难以阅读。例如,我觉得很烦人,因为对象是一种接口类型,所以不能按F12查看对象的实现。

我已经使用了多年的一个行之有效的模型,它非常容易维护,就是保持所有类名与表名相同,然后是与列名匹配的字段名。然后,只需在编辑器中使用搜索和替换,就可以轻松地生成方法,代码更改也是如此。这样就不需要在内存中保留列名和列索引。您只需在数据访问层中对它们进行硬编码(硬编码并不总是坏的!)。例如:

this.Price = reader["Price"] as decimal?;

这种方法的性能非常好,而且代码是超级可维护的!

无论采用何种方法,关键是如何保持从表列到类属性的映射。我建议对它们进行硬编码,并使用简单的命名约定(列名=属性名)。这种方法要求您在添加或更改列时重新编译,但我认为这些更改发生的频率不足以证明将列名放在变量中或从单独的文件中读取列名是合理的。这样做可以避免重新编译,但会降低代码的易用性。我认为这不值得。

看看AutoMapper。它是一个有用的小库,用于将DTO转换为POCO等,并将消除大量重复的代码和在应用程序层之间传输数据的工作量。

创建接口继承层次结构

interface ISchemaField<T>
{
    string Name { get; }
}
interface ITableField<T> : ISchemaField<T>
{
    int Index { get; }
}
interface IRecordField<T> : ITableField<T>
{
    T Value { get; set; }
}

实现这些接口的类可以遵循相同的继承方案。