我试图排除一些重复的代码,但它现在开始闻起来很时髦。假设我从这个开始并不是很正确,但你明白我的意思:
public virtual OrganisationEntity Get(int id)
{
SqlCommand command = new SqlCommand();
command.CommandText = @"SELECT t.Id, t.Description FROM Organisation t Where t.Id = @Id";
command.Parameters.Add("@id", SqlDbType.Int).Value = id;
List<OrganisationEntity> entities = new List<OrganisationEntity>();
SqlDataReader reader = Database.ExecuteQuery(command, ConnectionName.Dev);
while (reader.Read())
{
OrganisationEntityMapper mapper = Mapper;
entities = mapper.MapAll(reader);
}
return entities.First<OrganisationEntity>();
}
很明显,除了查询之外,每个其他Get(int id)方法都有相同的形式,所以我的下一步是创建一个基类,RepositoryBase如下所示:
public abstract class RepositoryBase<T> where T : new()
{
/// <summary>
///
/// </summary>
public abstract EntityMapperBase<T> Mapper { get; }
public virtual T Get(int id)
{
List<T> entities = new List<T>();
SqlDataReader reader = Database.ExecuteQuery(Command, ConnectionName);
while (reader.Read())
{
EntityMapperBase<T> mapper = Mapper;
entities = mapper.MapAll(reader);
}
return entities.First<T>();
}
}
以增加一些通用的趣味性,但这也是它变得丑陋的地方。首先,Database.ExecuteQuery需要一个SqlCommand和一个枚举,所以我想,好吧,然后我将添加两个属性,我将使用一些东西来启动它们。我意识到我不再需要这里的int id参数了,因为我在一个子类中构造了查询,所以我可能会将命令和connectionName作为参数传递,我希望connectionName无论如何都要依赖于OrganisationRepository (其他人需要另一个字符串):
public class OrganisationRepository : RepositoryBase<OrganisationEntity>
{
protected override EntityMapperBase<OrganisationEntity> Mapper
{
get
{
return new OrganisationMapper();
}
}
public override OrganisationEntity Get(int id)
{
SqlCommand command = new SqlCommand();
command.CommandText = @"SELECT t.Id, t.Description FROM Organisation t Where t.Id = @Id";
command.Parameters.Add("@id", SqlDbType.Int).Value = id;
return base.Get(command, ConnectionName.Dev);
}
}
但是,哦,当然,现在方法签名不再同步了……糟了!所以,基本上我想知道。只是感觉很难受,但不知道具体原因。一方面,我想尽可能多地剔除重复代码,但现在它留给我的是!
如何将其重构为(更)适当的OO?我是不是应该忘记分解查询字符串,写很多重复的东西呢?
发布于 2010-12-16 03:17:50
你的“下一步”不会和我的一样。
我的下一步将是找到您正在尝试重构的这种“通用代码”的另一个示例。也许是一个"`CustomerEntity.Get(int id)'“方法。
现在,让我们假设CustomerEntity和OrganisationEntity版本之间的唯一区别是查询字符串和将术语“组织”替换为“客户”。我的下一步是尝试使这两种方法越来越相同。假设此方法是OrganisationEntityRepository类的一部分,我会将其重构为EntityRepository1类,并将CustomerEntityRepository重构为EntityRepository2。
步骤1是为实体类型引入一个泛型参数。您必须对OrganisationEntityMapper和CustomerEntityMapper类执行相同的操作。
接下来,回过头来看看还有什么不同。我发现它们使用不同的映射器类,所以让我们将映射器类型设为泛型。为了做到这一点并仍然引用MapAll方法,我将引入一个带有MapAll方法的IMapper接口,并让我的两个具体的映射器类实现该接口。
现在,下一个最大的区别是查询。我将把它放到一个虚拟的"CommandText“属性中。
现在我想我已经为基类做好了准备,也许是EntityRepositoryBase<TEntity,TMapper>
。在适当的假设下,我得出了以下结论:
public abstract class EntityRepositoryBase<TEntity, TMapper>
where TMapper : IMapper<TEntity>
{
public virtual TEntity Get(int id)
{
List<TEntity> entities;
using (var command = new SqlCommand {CommandText = CommandText})
{
command.Parameters.Add("@id", SqlDbType.Int).Value = id;
entities = new List<TEntity>();
using (var reader = Database.ExecuteQuery(command, ConnectionName.Dev))
{
while (reader.Read())
{
var mapper = Mapper;
entities = mapper.MapAll(reader);
}
}
}
return entities.First();
}
protected abstract string CommandText { get; }
protected abstract TMapper Mapper { get; }
}
public class OrganisationEntityRepository :
EntityRepositoryBase<OrganisationEntity, OrganisationEntityMapper<OrganisationEntity>>
{
protected override string CommandText
{
get { return @"SELECT t.Id, t.Description FROM Organisation t Where t.Id = @Id"; }
}
protected override OrganisationEntityMapper<OrganisationEntity> Mapper
{
get { throw new NotImplementedException(); }
}
}
public class CustomerEntityRepository : EntityRepositoryBase<CustomerEntity, CustomerEntityMapper<CustomerEntity>>
{
protected override string CommandText
{
get { return @"SELECT t.Id, t.Description FROM Customer t Where t.Id = @Id"; }
}
protected override CustomerEntityMapper<CustomerEntity> Mapper
{
get { throw new NotImplementedException(); }
}
}
而且,不用说,尽管我还是要说:支持JetBrains ReSharper 5.1,因为它做了所有移动的事情,所以我不必这么做。
发布于 2010-12-16 03:17:19
我相信像Entity Framework或nHibernate这样的ORM会更适合解决这种情况。他们可以照顾所有的管道,你正在试图建立自己。
发布于 2010-12-16 03:25:44
如果您可以在项目中使用ORM框架,我认为这会比手工编写所有这些内容更可取。
但是,可以进行这种重构的一种方法是在RepositoryBase中使用一个具有以下签名的抽象方法:
public abstract T Get(int id);
还有一个带有这个签名的受保护的方法:
protected T Get(SqlCommand command, SqlConnection connection)
它的代码与您之前在RepositoryBase中显示的代码相同。
这样,在派生类中,您只需实现构造命令的类,并从执行实际数据库调用的基类调用命令。
https://stackoverflow.com/questions/4453746
复制相似问题