依赖注入 - 放置我的存储库对象的位置

时间:2013-11-27 16:57:35

标签: c# dependency-injection ninject repository-pattern

我目前正在转换大型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的具体实现。这是可以实现甚至推荐的吗?

2 个答案:

答案 0 :(得分:1)

您的实体不应该知道存储位置或方式的任何信息。存储库模式的想法是将持久性的责任从业务逻辑中移除。实际上,这意味着您将按如下方式设置服务:

  • 为每个“顶级”实体创建单独的存储库
  • 将存储库注入Web服务/控制器/业务对象
  • 将您的实体定义为具有状态和行为的常规业务对象
  • 让Web服务等使用存储库根据需要通过ID
  • 获取实体
  • 您不需要使用业务逻辑中的ID调用构造函数...这是一个错误的模式
  • 您仍然会使用构造函数来实例化您随后将提交给存储库的新实体,例如personRepository.Add(new Person(“Bob”))

这将带来额外的好处,即通过注入模拟的存储库使您的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

相关问题