【问题标题】:C# Design Pattern - Best Way to Design For Many Datasources [duplicate]C# 设计模式 - 设计许多数据源的最佳方法 [重复]
【发布时间】:2015-08-02 00:13:22
【问题描述】:

我目前有一个 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>();
             }
         }

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

【问题讨论】:

  • 具体的存储库是什么样的?你能举一个这种实现的例子吗?
  • 添加了一个通用示例。
  • 业务对象中是否同时需要所有三种类型的存储库?
  • 我不确定 SimpleInjector 是否允许属性注入。这将为您解决一个负担。

标签: c# asp.net asp.net-mvc design-patterns


【解决方案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 下编译。

我建议将 IRepository 更改为 IBusiness in Entity 并将数据访问问题从 IDataAccess 接口中分离出来,只有 IBusiness 实现者通过其构造函数接收。然后将 ISourceRepositoryContext 更改为 ISourceEntities 并将该接口中的 IRepository 属性更改为 IBusiness 属性。

BusinessLogic 类是我真正关心的部分。你确定这一堂课不会引起太多关注吗?这应该是 UoW 课程吗?

有关基于类似技术的更完整解决方案,请查看我对其他问题的回答:.NET Managing Layers Relationships

【讨论】:

  • @user2966445 我很乐意详细说明,但首先您能否提供 MyObject、SourceAResult、SourceBResult 和 SourceCResult 的类定义。您真的需要为您的 BusinessLogic 课程提供所有 200 个资源吗?还是 BusinessLogic 打算成为某种基类?
  • @user2966445 作为一般准则,如果您在一个类中添加超过 3-5 个依赖项,这通常被认为是代码异味,可能表明您的类承担了太多责任。您可能需要考虑重构它。
  • 谢谢,但我正在寻找一个实际的替代方案。我意识到有一个问题(因此提出这个问题)。该示例应提供足以理解该概念的内容。随着更多数据源的添加,构造函数也在增长。
  • @user2966445 不幸的是,该示例没有提供有关 MyObject 如何与 SourceAResult、SourceBResult 和 SourceCResult 相关的足够信息。如果没有这些信息,就很难分析您的情况并推荐替代方案
  • @user2966445 我已经对我的回答进行了更新,希望能为您提供解决方案,或者至少为您提供一些关于如何重构代码的想法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-27
  • 2013-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多