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");

私の問題は2行目にあります。私は自分の工場を次のように使用したいと思います。

// 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のようなものを使用してこれを処理する場合は、ファクトリクラスの複雑さが時間の経過とともに大幅に増大する可能性が高いため、時間の投資がより適切になります。

Br。モーテン

于 2012-11-17T16:41:47.957 に答える
1

There are 3 problems with your code:

First is IEntity. Having single type of ID is against DDD, because in DDD, identity of object is given by domain and it can be anything from string, int, guid to complex type.

Second is generic repository with IRepository, which again, is highly useless, because you will rarely pass this interface and mostly will pass interface for repository for concrete entity.

Third thing is that in DDD repositories should exist only for aggregate roots, which is not reflected in your design.

If you fix this, you will find out, that implementation of interface of repository for specific entity can easily be suplied by DI framework.

于 2012-11-17T16:28:55.087 に答える