C# 依赖项注入-将我的存储库对象放在何处

C# 依赖项注入-将我的存储库对象放在何处,c#,dependency-injection,ninject,repository-pattern,C#,Dependency Injection,Ninject,Repository Pattern,我目前正在将一个大型web服务转换为使用存储库模式和依赖项注入。我们正在扩大我们的团队,可靠的单元测试的好处超过了重构代码所需的努力 根据一位同事的推荐,我选择了Ninject作为我的框架,并开始重构我的代码。这涉及到创建一个包含对象本身的“公共”项目、一个包含数据访问逻辑的Repository.Database项目,以及一个同时使用这两个对象的web服务。我使用了基于约定的映射,以便IPersonRepository应该映射到我的具体PersonRepository类 我目前正在使用[Inje

我目前正在将一个大型web服务转换为使用存储库模式和依赖项注入。我们正在扩大我们的团队,可靠的单元测试的好处超过了重构代码所需的努力

根据一位同事的推荐,我选择了Ninject作为我的框架,并开始重构我的代码。这涉及到创建一个包含对象本身的“公共”项目、一个包含数据访问逻辑的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;
}
你可能会明白我的难题。如何使用构造函数而不必从存储库返回新对象,然后将每个字段映射到当前对象?我不能替换“this”,虽然我可以使用AutoMapper之类的工具一次性映射每个字段,但感觉我在这里做了一些固有的错误

我可以使用静态方法代替注射器:

[Inject]
public static IPersonRepository Repository { get; set; }

public string Name;

public static Person GetByID(int ID)
{
    return Repository.Get(ID);
}
但正如您所看到的,它需要使存储库成为静态的,并且感觉我应该使用构造函数而不是静态的“GetByID”方法。这可能只是因为我已经习惯了使用构造函数

或者,我可以将存储库传递到Person构造函数中,但同样,每次在代码中实例化Person时都会感觉很混乱


我试图实现的是让我现有的WCF项目使用一个存储库加载其所有数据,让我的单元测试项目使用另一个存储库加载其所有数据。我不想在这两个方面都传递IPersonRepository的具体实现。这是可以实现的,甚至是推荐的吗?

您的实体不需要知道它们存储在何处或如何存储。存储库模式的思想是将持久性的责任从业务逻辑中分离出来。实际上,这意味着您将按照以下方式设置您的服务:

  • 为每个“顶级”实体创建单独的存储库
  • 将存储库注入web服务/控制器/业务对象
  • 将实体定义为具有状态和行为的常规业务对象
  • 让web服务等使用存储库通过ID获取所需的实体
  • 您不需要使用业务逻辑中的ID调用构造函数……这是一种糟糕的模式
  • 您仍然可以使用构造函数实例化一个新实体,然后将其提交到存储库,例如personRepository.Add(newperson(“Bob”))

这样做还有一个额外的好处,就是通过注入模拟存储库使您的web服务可测试,而不必担心在代码中如何检索实体。

我创建了一个小型助手类:“EntityResolver”,它实现了“IValueResolver”最近在AutoMapper中引入的接口。当提供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))
    ;
Mapper.CreateMap()
.ForMember(e=>e.EmployeeNumber,opt=>opt.MapFrom(vm=>vm.Number))
//其他物业略去
.formMember(e=>e.Company,opt=>opt.resolvusing().FromMember(vm=>vm.CompanyId))
;
2)实体解决方案的代码

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);
    }
}
public类EntityResolver:IValueResolver其中tenty:class,ienty,new()
{
公共解决结果解决(解决结果源)
{
返回source.New(ResolveObject(source));
}
私有对象ResolutionObject(ResolutionResult源)
{
if(!source.Context.Options.Items.ContainsKey(“服务”))返回null;
var services=(List)source.Context.Options.Items[“services”];
var item=services.FirstOrDefault(s=>s为IBaseService);
如果(item==null)返回null;
var id=(长)source.Value;
如果(id 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;
}
受保护的覆盖列表GetServices()
{
var services=base.GetServices();
服务。添加(_companyService);
服务。添加(_functionService);
服务。添加(_子功能服务);
服务。添加(_countryService);
返回服务;
}

有关更多详细信息,请参阅。

域模型对象不应依赖于您的存储库该类依赖IPersonRepository,但不依赖其实现。这不可接受吗?不,这不好。
Person
不应该知道
IPersonRepository
我认为您的答案和项目符号有一些冲突,特别是“存储库模式的思想是将持久性的责任从业务逻辑中移开。”,但在bullet#2中,您说:“将存储库注入到您的web服务/控制器/业务对象中--将回购注入业务对象不会与从业务逻辑中承担持久性责任直接冲突吗?我认为这不会冲突,但我想你可以读一下“责任”那样的话……我的意思是,业务对象应该能够启动持久性,而不必担心持久性实际上是如何工作的或在哪里工作的
protected override List<object> GetServices()
{
    var services = base.GetServices();
    services.Add(_companyService);
    services.Add(_functionService);
    services.Add(_subfunctionService);
    services.Add(_countryService);

    return services;
}