这是我的域模型和通用存储库的设计
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的要求。
有没有办法将此参数与客户端隔离?
答案 0 :(得分: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之类的东西来为您处理此问题,那么您将更好地投入时间,因为您很可能会发现工厂类的复杂性会随着时间的推移而显着增加。
溴。的Morten
答案 1 :(得分:1)
您的代码有3个问题:
首先是IEntity
。具有单一类型的ID是针对DDD的,因为在DDD中,对象的标识由域给出,它可以是从string,int,guid到复杂类型的任何东西。
其次是具有IRepository
的通用存储库,这也是非常无用的,因为您很少会传递此接口,并且通常会为具体实体的存储库传递接口。
第三件事是DDD存储库只应存在于聚合根中,这不会反映在您的设计中。
如果你解决这个问题,你会发现DI框架可以很容易地实现特定实体的存储库接口的实现。