我的数据访问层我想使用DTO并确保所有网关(表数据网关)都将实现返回特定于给定网关的某些DTO列表的方法。 为了实现这一点,我创建了空界面,就像这样
public interface IDataTransferObject{}
这样的所有DTO实现了这个界面
public class UserAccountTypeDTO : IDataTransferObject
{
public int Id { get; set; }
public int AccountTitle { get; set; }
public int CreditTypeId { get; set; }
public bool Active { get; set; }
}
然后我为所有网关制作了通用接口
public interface IDefaultGateway<D> where D:IDataTransferObject
{
List<D> Read(SqlDataReader sqlReader);
}
最后通过我的网关实施了IDefaultGateway
class UserAccountTypeGW : IDefaultGateway<UserAccountTypeDTO>
{
private const string Table = " UserAccountType ";
private string SelectAll = "select * from" + Table + "";
private string SelectById = ...;
public List<UserAccountTypeDTO> Read(SqlDataReader sqlReader)
{
throw new NotImplementedException();
}
}
即使他们不共享任何行为,是否可以将空接口仅以某种方式组合在一起?
网关中的字段只是其值正在变化。我确实将字段复制并粘贴到每个网关,我想知道是否有更快/更懒的方式来执行此操作。
答案 0 :(得分:3)
您需要搜索的字词称为Marker Interfaces
。这是MSDN的指南:
使用标记接口(没有成员的接口)进行避免。 如果您需要将类标记为具有特定特征(标记),通常使用自定义属性而不是接口。
话虽如此,检查一个类是否实现接口比检查它是否具有属性更容易。
正如@Groo在本回答的评论部分提到的那样,这是一个非常好的观点:
与属性
相比,Marker接口至少也提供了一些编译时检查
答案 1 :(得分:1)
关于问题的第二部分(如何避免代码重复),这就是抽象类的用途。
您可以提供一些默认值,派生类可以根据需要覆盖这些值:
abstract class BaseGateway<T> : IDefaultGateway<T> where T : IDataTransferObject
{
readonly string _tableName;
readonly string _selectAll;
public BaseGateway()
{
// default table name
_tableName = this.GetType().Name.Replace("DTO", "");
_selectAll = $"select * from {_tableName}";
}
// these members are virtual, so that they can be overriden
protected virtual string TableName => _tableName;
protected virtual string SelectAll => _selectAll;
// derived classes should implement their own 'Read' method
public abstract List<T> Read(IDataReader sqlReader);
}
但是,请注意,执行此类操作会使您的代码容易受到SQL注入攻击。最好使用ORM,或至少使用微型&#34; ORM如Dapper。
Dapper通过几种简单的IDbConnection
扩展方法为您提供了这种简单性:
public class Dog
{
public int Age { get; set; }
public string Name { get; set; }
}
using (IDbConnection conn = OpenConnection())
{
var dog = conn
.Query<Dog>("Select * from Dog where Age = @Age", new { Age = 10 })
.FirstOrDefault();
}
答案 2 :(得分:1)
避免使用这些标记接口。现在使用标记接口的唯一“好”理由是,如果您计划使用反射操作某些类型的对象(这次不是这种情况)。
在这种情况下 - 抽象类似乎是最好的选择。使用默认实现在抽象类和属性中定义Read()
方法。根据需要在所有后代中使用virtual
方法覆盖它。这样你甚至不需要一个包装类来读取每个特定类型的对象,因为你将能够引用该对象并直接在其上调用Read()
。