C#设计模式-设计多个数据源的最佳方法

4
我目前有一个ASP.Net MVC 5应用程序,它使用3个外部数据源(调用外部API,反序列化响应并映射到业务POCO)。
该应用程序当前使用SimpleInjector将每个数据源的具体存储库注入到业务逻辑层以供使用。
问题是,随着更多的数据源被添加(可能达到20-30个),构造函数会变得非常庞大,并且注入所有这些存储库似乎很麻烦。
除了使用不同的存储库之外,是否有更好的模式/方法来使用所有数据源?
门面或其他模式是否更合适?
非常通用的示例:
    public class MyObject(){
         public IEnumerable<Cat> Cats { get; set; }
         public IEnumerable<Dog> Dogs { get; set; }
         public IEnumerable<Fish> Fish { get; set; }

    }

        public class BusinessLogic{
               private readonly ISourceARepository _sourceA;
               private readonly ISourceBRepository _sourceB;
               private readonly ISourceCRepository _sourceC;

            public BusinessLogic(ISourceARepository sourceA, ISourceBRepository sourceB, ISourceCRepository sourceC){
                    _sourceA = sourceA;
                    _sourceB = sourceB;
                    _sourceC = sourceC;
}

private Dog MapSourceARecordToDog(SourceARecord record){
    var result = new Dog();    

    if(record != null){
        result.Name = record.NameField;
        result.Age = record.Age;
    }     

    return result;
}

private Cat MapSourceBRecordToCat(SourceBRecord record){
    var result = new Cat();

    if(record != null){
         result.Name = record.NameField;
         result.Weight = record.WeightField;
    }

    return result;
}

private Fish MapSourceCRecordToFish(SourceCRecord record){
    var result = new Fish();

    if(record != null){
        result.ID = record.IDField;
        result.Name = record.NameField;

    }

    return result;
}

            public MyObject GetResults(){
                  var result = new MyObject();

                  result.Dogs = _sourceA.GetAll().Select(MapSourceARecordToDog).ToList();
                  result.Cats = _sourceB.GetAll().Select(MapSourceBRecordToCat).ToList();
                  result.Fish = _sourceC.GetAll().Select(MapSourceCRecordToFish).ToList();
                  return result;
            }
        }

        public class SourceARespository : ISourceARepository{
             public IEnumerable<SourceAResult> GetAll(){
                 return new List<SourceAResult>();
             }
         }

        public class SourceBRespository : ISourceBRepository{
             public IEnumerable<SourceBResult> GetAll(){
                 return new List<SourceBResult>();
             }
         }

        public class SourceCRespository : ISourceCRepository{
             public IEnumerable<SourceCResult> GetAll(){
                 return new List<SourceCResult>();
             }
         }

更新: 这不是构造函数混乱问题的重复,因为在这种情况下,一个类需要许多不同的数据源,但仍然具有单一职责。因此,它值得拥有自己的解释和答案。

具体的存储库是什么样子?你能给一个这样实现的例子吗? - Ric .Net
添加了一个通用示例。 - user2966445
在业务对象中,您是否需要同时使用这三种类型的存储库? - Aniket Inge
我不确定SimpleInjector是否允许属性注入。如果可以的话,那会为您解决一个负担。 - Aniket Inge
1个回答

1
你应该只向依赖它的使用者注入一个实体仓库。你也可以选择使用业务类中介来适配仓库。
更新:根据问题陈述和提供的信息,这里有一个可能的解决方案。将核心基础设施定义如下:
public abstract class Entity<TEntity, TDomainObject, TIRepository>
    where TEntity       : Entity<TEntity, TDomainObject, TIRepository>
    where TDomainObject : Entity<TEntity, TDomainObject, TIRepository>.BaseDomainObject, new()
    where TIRepository  : Entity<TEntity, TDomainObject, TIRepository>.IBaseRepository
{

    public class BaseDomainObject {}

    public interface IBaseRepository
    {
        IEnumerable<TDomainObject> GetAll();
        IEnumerable<T> GetAllMapped<T>(Func<TDomainObject, T> mapper);
    }

    public class BaseRepository : IBaseRepository
    {
        public IEnumerable<TDomainObject> GetAll()
        {
            return new List<TDomainObject>();
        }
        public IEnumerable<T> GetAllMapped<T>(Func<TDomainObject, T> mapper)
        {
            return this.GetAll().Select(mapper);
        }
    }

}

请定义您的源实体,如下所示:

public class SourceA : Entity<SourceA, SourceA.DomainObject, SourceA.IRepository>
{
    public class DomainObject : BaseDomainObject
    {
        public  string  Name;
        public  int     Age;
    }
    public interface IRepository : IBaseRepository {}
    public class Repository : BaseRepository, IRepository {}
}

public class SourceB : Entity<SourceB, SourceB.DomainObject, SourceB.IRepository>
{
    public class DomainObject : BaseDomainObject
    {
        public  string  Name;
        public  decimal Weight;
    }
    public interface IRepository : IBaseRepository {}
    public class Repository : BaseRepository, IRepository {}
}

public class SourceC : Entity<SourceC, SourceC.DomainObject, SourceC.IRepository>
{
    public class DomainObject : BaseDomainObject
    {
        public  Guid    Id;
        public  string  Name;
    }
    public interface IRepository : IBaseRepository {}
    public class Repository : BaseRepository, IRepository {}
}

然后定义一个 ISourceRepositoryContext 接口,像这样将每个源代码库接口添加到此处:

public interface ISourceRepositoryContext
{

    SourceA.IRepository SourceARepository { get; }
    SourceB.IRepository SourceBRepository { get; }
    SourceC.IRepository SourceCRepository { get; }

}

然后为接口定义一个默认实现:

public class DefaultSourceRepositoryContext : ISourceRepositoryContext
{
    public SourceA.IRepository SourceARepository => new SourceA.Repository();
    public SourceB.IRepository SourceBRepository => new SourceB.Repository();
    public SourceC.IRepository SourceCRepository => new SourceC.Repository();
}

定义你的结果传输对象:
public class Dog
{
    public  string  Name;
    public  int     Age;
}

public class Cat
{
    public  string  Name;
    public  decimal Weight;
}

public class Fish
{
    public  Guid    Id;
    public  string  Name;
}

public class MyObject
{
     public IEnumerable<Cat>  Cats { get; set; }
     public IEnumerable<Dog>  Dogs { get; set; }
     public IEnumerable<Fish> Fish { get; set; }
}

然后在你的BusinessLogic类中使用ISourceRepositoryContext:

public class BusinessLogic
{
    protected ISourceRepositoryContext repositories;

    public BusinessLogic(ISourceRepositoryContext repositories)
    {
        this.repositories = repositories;
    }

    public MyObject GetResults(string param1)
    {
        return new MyObject()
        {
            Dogs    = this.repositories.SourceARepository.GetAllMapped
            (domainObject=>new Dog
            {
                Age     = domainObject.Age,
                Name    = domainObject.Name
            }),

            Cats    = this.repositories.SourceBRepository.GetAllMapped
            (domainObject=>new Cat
            {
                Name    = domainObject.Name,
                Weight  = domainObject.Weight
            }),

            Fish    = this.repositories.SourceCRepository.GetAllMapped
            (domainObject=>new Fish
            {
                Id      = domainObject.Id,
                Name    = domainObject.Name
            }),
        };
    }
}

我确认以上内容可以在C# 6.0下编译。
我建议将Entity中的IRepository更改为IBusiness,并将数据访问关注点从其中拆分出来,形成一个仅由IBusiness实现者通过构造函数接收的IDataAccess接口。然后将ISourceRepositoryContext更改为ISourceEntities,并将该接口中的IRepository属性更改为IBusiness属性。
BusinessLogic类是我真正关心的部分。您确定这个类不会承担太多的职责吗?这应该是一个UoW类吗?
对于基于类似技术的更完整解决方案,请查看我对另一个问题的答案:.NET Managing Layers Relationships

@user2966445,我很乐意详细说明,但首先您能否提供MyObject、SourceAResult、SourceBResult和SourceCResult的类定义。您的BusinessLogic类是否真的需要所有200个源?还是BusinessLogic旨在成为某种基类? - Tyree Jackson
@user2966445,不幸的是,这个示例没有提供足够的信息来说明MyObject与SourceAResult、SourceBResult和SourceCResult之间的关系。没有这些信息,很难分析您的情况并推荐替代方案。 - Tyree Jackson
@user2966445,我刚看到你的更新。我会进行调整,以尽可能DRY地实现你想要完成的目标。 - Tyree Jackson
让我们在聊天中继续这个讨论 - Tyree Jackson
@user2966445 答案已更新以包含您在最后一次更新中表达的细节和意图。 - Tyree Jackson
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接