3

这是我对域模型和通用存储库的设计

public interface IEntity 
{
     long Id { get; }
}

public interface IRepository<T> where T : class, IEntity, new()
{
     void Save(T entity);
     void Delete(long id);
     T Get(long id);
     IEnumerable<T> GetAll();
}

public interface IUserRepository : IRepository<User> 
{
     User Login(string username, string password);
}

public class User : IEntity
{
     // Implementation of User
}

public abstract class BaseRepository<T> : IRepository<T> where T : class, IEntity, new()
{
      // Implementation of IRepository
}

public class UserRepository : BaseRepository<User>, IUserRepository
{
      // Implementation of IUserRepository
      // Override BaseRepository if required
}

当我想实例化一个存储库实例时,我使用一个实现以下接口的工厂

public interface IRepositoryFactory
{
     R CreateRepository<R, T>()
          where R : IRepository<T>
          where T : class, IEntity, new();
}

并使用工厂对象如下

1. IRepositoryFactory factory = CreateFactory();
2. IUserRepository repository = factory.CreateRepository<IUserRepository, User>();
3. User user = repository.Login("user", "1234");

我的问题在第二行。我想用我的工厂就好了。

// Without specifying the User type parameter
factory.CreateRepository<IUserRepository>()

由于我的 IRepository 接口对实体类型有约束,我的工厂使用相同的约束来满足 IRepository 要求。

有没有办法将此参数与客户端隔离?

4

2 回答 2

2

我同意其他人的观点,您将从查看 Ninject 之类的 DI/IoC 框架中受益。

所以这个答案并不是不遵循其他建议的建议。但是,仍然有一些方法可以在较低的级别上解决您的问题。这段代码没有经过很好的测试,但你可以这样做:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using NUnit.Framework;

namespace TestConsole1
{
  public interface IEntity
  {
    long Id { get; }
  }

  public interface IRepository<T> where T : class, IEntity, new()
  {
    void Save(T entity);
    void Delete(long id);
    T Get(long id);
    IEnumerable<T> GetAll();
  }

  public interface IUserRepository : IRepository<User>
  {
    User Login(string username, string password);
  }

  public class User : IEntity
  {

    // Implementation of User
    public long Id
    {
      get { return 42; }
    }
  }

  public abstract class BaseRepository<T> : IRepository<T> where T : class, IEntity, new()
  {
    // Implementation of IRepository
    public void Save(T entity)
    {
      throw new NotImplementedException();
    }

    public void Delete(long id)
    {
      throw new NotImplementedException();
    }

    public T Get(long id)
    {
      throw new NotImplementedException();
    }

    public IEnumerable<T> GetAll()
    {
      throw new NotImplementedException();
    }
  }

  public class UserRepository : BaseRepository<User>, IUserRepository
  {
    // Implementation of IUserRepository
    // Override BaseRepository if required
    public User Login(string username, string password)
    {
      return new User();
    }
  }

  class Factory 
  {
    public T CreateRepository<T>() where T : class 
    {
      //TODO: Implement some caching to avoid overhead of repeated reflection
      var abstractType = typeof(T);
      var types = AppDomain.CurrentDomain.GetAssemblies().ToList()
          .SelectMany(s => s.GetTypes())
          .Where(p => p.IsClass && 
                      !p.IsAbstract && 
                      abstractType.IsAssignableFrom(p));

      var concreteType = types.FirstOrDefault();
      if (concreteType == null)
        throw new InvalidOperationException(String.Format("No implementation of {0} was found", abstractType));

      return Activator.CreateInstance(concreteType) as T;
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      var factory = new Factory();
      var userRepo = factory.CreateRepository<IUserRepository>();
      Console.WriteLine(userRepo.GetType());
      User user = userRepo.Login("name", "pwd");
      Console.WriteLine(user.Id);
      Console.ReadKey();
    }
  }
}

正如这段代码所揭示的,一个中心点是您需要处理接口和具体类之间的耦合,例如 IUserRepository 和 UserRepository 之间的耦合。如果您不通过映射器或类似方式直接处理此关系,您可以实现一种更自动的方式,如代码中所示的方式。

但是,如果您使用Ninject之类的东西来代替您处理这个问题,这将是您更好的时间投资,因为您很可能会发现您的工厂类的复杂性会随着时间的推移而显着增加。

兄弟。莫腾

于 2012-11-17T16:41:47.957 回答
1

您的代码存在 3 个问题:

首先是IEntity。拥有单一类型的 ID 是违反 DDD 的,因为在 DDD 中,对象的身份由域给出,它可以是从字符串、int、guid 到复杂类型的任何东西。

其次是带有 的通用存储库IRepository,这同样非常无用,因为您很少会通过此接口,并且大多数情况下会通过具体实体的存储库接口。

第三件事是,在 DDD 存储库中应该只存在于聚合根,这不会反映在您的设计中。

如果你解决了这个问题,你会发现,特定实体的存储库接口的实现可以很容易地由 DI 框架提供。

于 2012-11-17T16:28:55.087 回答