Отправить зависимость модели IRepository при чтении данных из Mongo - PullRequest
0 голосов
/ 06 ноября 2018

В моем приложении я использую Autofac ContainerBuilder, который регистрирует мои репозитории ...

У меня также есть класс CustomerModel, который представляет коллекцию MongoDB. Он также имеет зависимость IRepository для другой модели предметной области:

public class CustomerModel
{
    private readonly IRepository<OrderModel> _ordeRepository;

    public CustomerModel(IRepository<OrderModel> ordeRepository)
    {
        _ordeRepository = ordeRepository;
    }

    [BsonId]
    public ObjectId Id;

    public string Email;

    ...

    public List<OrderModel> Orders
    {
        get { return _ordeRepository.List(x => x.CustomerId == Id); }
    }
}

Как я могу внедрить зависимость IRepository <> при чтении данных из Mongo?

public interface IRepository<TEntity>
{
    IList<TEntity> List(Expression<Func<TEntity, bool>> expression);
}

public class MongoRepository<TEntity> : IRepository<TEntity>
{
    public IList<TEntity> List(Expression<Func<TEntity, bool>> expression)
    {
        IMongoCollection<TEntity> collection = ...;
        return collection.FindSync(expression).ToList();
    }
}

1 Ответ

0 голосов
/ 06 ноября 2018

Вы не должны иметь реализацию, непосредственно связанную с вашей собственностью. Причина в том, что сущность - это представление ваших данных, а сущности не волнует, как элемент хранится или создается. Просто представление вашего бизнес-объекта.

Другая проблема заключается в том, что вы нарушаете более распространенную практику следования доменному дизайну, согласно определению Мартина Фаулера и Эванса. Одним из самых серьезных нарушений может быть тот факт, что вы навязываете логику в свойстве, если вы вызываете свойство, то вы автоматически попадаете в базу данных.

Как обозначено авторами C # подробно:

Для каждого типа, который вы пишете, вы должны учитывать его интерфейс остальной мир (включая занятия в одной сборке). это это описание того, что он делает доступным, его внешность. Реализация не должна быть частью этого описания, не более обязательно должно быть. (Вот почему я предпочитаю композицию наследованию, где выбор имеет смысл - наследование обычно выставляет или ограничивает возможные реализации.)

Свойство передает идею «Я сделаю ценность доступной для вы или принимаете ценность от вас. "Это не концепция реализации, это концепция интерфейса. Поле, с другой стороны, связывает реализация - он говорит: «этот тип представляет значение в этом очень специфический способ ". Там нет инкапсуляции, это пустое хранилище формат. Это часть причины, по которой поля не являются частью интерфейсов - им там не место, так как они говорят о том, как что-то достигается а не то, что достигается.

Я вполне согласен с тем, что большую часть времени поля действительно могут быть использованы без проблем во время жизни приложения. Просто не понятно заранее, какие времена, и это все еще нарушает дизайн принцип не подвергая реализации.

Чтобы справиться с вашей реализацией, придерживаясь принципов проектирования, может лучше подходить следующий подход и / или стиль архитектуры. Пожалуйста, помните, шаблоны не печенья. Они решают конкретные проблемы по определенным причинам, они представляют сложность и свои собственные проблемы. Вы должны взвесить достоинства решения на основе вашего заявления.

// Repositories:
public interface ICustomerRepository : IDisposable
{
     IEnumerable<CustomerModel> RetrieveAllCustomers();

     CustomerModel RetrieveCustomerWithOrders(int id);
}

// Context:
public class CustomerContext : ICustomerRepository
{
     private bool disposed = false;
     private readonly IDbConnection dbConnection;

     public CustomerContext(IConfiguration configuration) => dbConnection = configuration.GetConnectionString("dbConnection");

     public IEnumerable<CustomerModel> RetrieveAllCustomers() => dbConnection.Query<CustomerModel>(query);

     public CustomerModel RetrieveCustomerWithOrders(int id) => dbConnection.Query<CustomerModel, OrderModel, CustomerModel>(query, (customer, order) =>
     {
          customer.Orders = order;
          return customer;
     }, new { CustomerId = id });

     public virtual bool Dispose(bool disposing)
     {
         if(!disposed)
         {
              if(disposing)
                  dbConnection.Dispose();

              disposed = true;
         }
     }

     public void Dispose() 
     {
          Dispose(true);
          GC.SuppressFinalize(this);
     }

     ~CustomerContext() => Dispose(true);     
}

// Factory:
public class CustomerFactory : ICustomerFactory
{
     private readonly IConfiguration configuration;

     public CustomerFactory(IConfiguration configuration) => this.configuration = configuration;

     public ICustomerRepository InstantiateCustomer() => new CustomerContext(configuration);
}

public interface ICustomerFactory
{
     ICustomerRepository InstantiateCustomer();
}

Итак, мы создали некоторую абстракцию, мы создали наш контейнер, у нас есть четкие контракты с соответствующими реализациями. Поэтому, если кто-то хочет использовать то, что вы создали, это будет выглядеть следующим образом:

public class CustomerService
{
     private readonly ICustomerFactory customerFactory;
     private readonly IConfiguration configuration;

     public CustomerService(ICustomerFactory customerFactory, IConfiguration configuration)
     {
          this.customerFactory = customerFactory;
          this.configuration = configuration;
     }

     public IEnumerable<CustomerModel> GetAllCustomers()
     {
         using(var customerContext = customerFactory.InstantiateCustomer(configuration))
              return customerContext.RetrieveAllCustomers();
     }

     public CustomerModel GetCustomerOrders(CustomerModel customer)
     {
          using(var customerContext = customerFactory.InstantiateCustomer(configuration))
              return customerContext.RetrieveCustomerWithOrders(customer.Id);
     }
}

Поэтому, когда они взаимодействуют с реализацией вашего сервиса, это создаст ясность, но также обеспечит ясный, лаконичный и выразительный поток кода. Разработчик может ясно видеть, что происходит в реализации сервиса, но совершенно не замечает, как происходит реализация данных.

Я не добавил обработки ошибок или нулевых проверок, но, надеюсь, это поможет вам.

...