我目前正在转换大型Web服务以使用存储库模式和依赖注入。我们正在扩展我们的团队,可靠的单元测试的好处超过了重构代码所需的工作量。
我根据同事的建议选择了Ninject作为我的框架,并开始重构我的代码。这包括创建一个包含对象本身的“Common”项目,一个包含数据访问逻辑的Repository.Database项目,以及一个使用这两者的Web服务。我使用了基于约定的映射,以便IPersonRepository应该映射到我具体的PersonRepository类。
我目前采用的方法是使用[Inject]属性在每个类上创建一个“Repository”属性,然后替换我的构造函数以使用所述存储库,但是遇到了我的第一个绊脚石,我不相信我在做什么事情是正确的。在我开始这一切之前,我会像这样实例化一个对象:
var p = new Person(ID);
使用我建议的格式,我的课看起来像这样:
[Inject]
public IPersonRepository Repository { get; set; }
public string Name;
public Person(int ID)
{
// This feels wrong
var p = Repository.Get(ID);
Name = p.Name;
}
你可能会看到我的难题。如何使用构造函数而不必从存储库返回新对象,然后将每个字段映射到当前对象?我无法取代“这个”,虽然我可以使用像AutoMapper这样的东西来一次映射每个字段,但感觉我在这里做了一些天生错误。
我可以使用静态方法而不是注入器:
[Inject]
public static IPersonRepository Repository { get; set; }
public string Name;
public static Person GetByID(int ID)
{
return Repository.Get(ID);
}
但正如您所看到的那样,它需要使Repository成为静态,并且感觉我应该使用构造函数而不是静态的“GetByID”方法。那可能只是因为我习惯使用构造函数。
或者我可以将Repository传递给Person构造函数,但是每次我在代码中实例化Person时都会感觉很麻烦。
我想要实现的是让我现有的WCF项目使用一个存储库加载其所有数据,并使我的Unit Test项目使用另一个加载所有数据。我不想在任何一个中传递IPersonRepository的具体实现。这是可以实现甚至推荐的吗?
答案 0 :(得分:1)
您的实体不应该知道存储位置或方式的任何信息。存储库模式的想法是将持久性的责任从业务逻辑中移除。实际上,这意味着您将按如下方式设置服务:
这将带来额外的好处,即通过注入模拟的存储库使您的Web服务可测试,而不必担心如何在代码本身中检索实体。
答案 1 :(得分:0)
我创建了一个小助手类:“EntityResolver”,它实现了最近在AutoMapper中引入的“IValueResolver”接口。当提供Id时,此助手类可以从存储库中检索实体。
1)将ViewModel映射到实体的AutoMapper配置定义如下:
Mapper.CreateMap<EmployeeVM, Employee>()
.ForMember(e => e.EmployeeNumber, opt => opt.MapFrom(vm => vm.Number))
// Other propeties omitted
.ForMember(e => e.Company, opt => opt.ResolveUsing<EntityResolver<Company>>().FromMember(vm => vm.CompanyId))
;
2)EntityResolver的代码
public class EntityResolver<TEntity> : IValueResolver where TEntity : class, IEntity, new()
{
public ResolutionResult Resolve(ResolutionResult source)
{
return source.New(ResolveObject(source));
}
private object ResolveObject(ResolutionResult source)
{
if (!source.Context.Options.Items.ContainsKey("Services")) return null;
var services = (List<object>)source.Context.Options.Items["Services"];
var item = services.FirstOrDefault(s => s is IBaseService<TEntity>);
if (item == null) return null;
var id = (long)source.Value;
if (id <= 0) return null;
var service = (IBaseService<TEntity>)item;
return service.GetById(id);
}
}
3)将ViewModel映射到实体时,我提供了额外的IMappingOperationOptions
Mapper.Map<TEntity>(viewModel, opt => opt.Items["Services"] = GetServices());
4)GetServices方法只返回解析Employee对象中使用的实体所需的所有服务。
protected override List<object> GetServices()
{
var services = base.GetServices();
services.Add(_companyService);
services.Add(_functionService);
services.Add(_subfunctionService);
services.Add(_countryService);
return services;
}
有关详细信息,请参阅my test project。