寻找一种适当的方式来实现我的通用工厂

时间:2011-04-18 09:20:15

标签: c#-4.0 factory-pattern

我正在努力实现工厂对象。这是上下文:

我在一个项目中有一个自定义商店。为了读/写记录,我将这段代码写在POCO模型/分离的存储库中:

public class Id { /* skip for clarity*/} // My custom ID representation

public interface IId
{
    Id Id { get; set; }
}
public interface IGenericRepository<T> where T : IId
{
    T Get(Id objectID);
    void Save(T @object);
}
public interface IContext
{
    TRepository GetRepository<T, TRepository>() 
        where TRepository : IGenericRepository<T> 
        where T:IId;
    IGenericRepository<T> GetRepository<T>() 
        where T:IId;
}

我的IContext接口定义了两种存储库。 前者用于仅带有get / save方法的标准对象,后者允许我为特定类型的对象定义特定方法。例如:

public interface IWebServiceLogRepository : IGenericRepository<WebServiceLog>
{
    ICollection<WebServiceLog> GetOpenLogs(Id objectID);
}

消费代码我可以做到这一点:

  • MyContext.GetRepository<Customer>().Get(myID); - &gt;标准获取
  • MyContext.GetRepository<WebServiceLog, IWebServiceLogRepository>().GetOpenLogs(myID); - &gt;具体操作

由于大多数对象存储库仅限于获取和保存操作,因此我编写了一个通用存储库:

public class BaseRepository<T> : IGenericRepository<T>
    where T : IId, new()
{
    public virtual T Get(Id objectID){ /* provider specific */ }
    public void Save(T @object) { /* provider specific */ }
}

,对于自定义的,我只是继承了基础存储库:

internal class WebServiceLogRepository: BaseRepository<WebServiceLog>, IWebServiceLogRepository
{
    public ICollection<WebServiceLog> GetByOpenLogsByRecordID(Id objectID)
    {
        /* provider specific */
    }
}

以上所有内容都可以(至少我认为没关系)。我现在正在努力实现MyContext类。我在我的项目中使用MEF用于其他目的。但由于MEF不支持(尚未)通用出口,我没有找到达到目标的方法。

我的上下文类现在看起来像是:

[Export(typeof(IContext))]
public class UpdateContext : IContext
{
    private System.Collections.Generic.Dictionary<Type, object> m_Implementations;

    public UpdateContext()
    {
        m_Implementations = new System.Collections.Generic.Dictionary<Type, object>();
    }
    public TRepository GetRepository<T, TRepository>()
        where T : IId
        where TRepository : IGenericRepository<T>
    {
        var tType = typeof(T);
        if (!m_Implementations.ContainsKey(tType))
        {
            /* this code is neither working nor elegant for me */
            var resultType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(
                (a) => a.GetTypes()
                ).Where((t)=>t.GetInterfaces().Contains(typeof(TRepository))).Single();

            var result = (TRepository)resultType.InvokeMember("new", System.Reflection.BindingFlags.CreateInstance, null, null, new object[] { this });

            m_Implementations.Add(tType, result);
        }
        return (TRepository)m_Implementations[tType];
    }

    public IGenericRepository<T> GetRepository<T>() where T : IId
    {
        return GetRepository<T, IGenericRepository<T>>();
    }
}

我很感激帮助我解决这个相当常见的情况

1 个答案:

答案 0 :(得分:1)

不确定我是否理解正确,但我认为你可能过于复杂化了。首先,确保您设计的代码独立于任何工厂或依赖注入框架或组合框架。

对于初学者,让我们看看你希望你的调用代码看起来像什么,这就是你所说的:

MyContext.GetRepository<Customer>().Get(myID); --> standard get
MyContext.GetRepository<WebServiceLog, IWebServiceLogRepository>().GetOpenLogs(myID);

您不必同意下面的命名选项,但它表明了我的代码,我可以告诉我,如果我错了。现在,我觉得这样的调用会更简单:

RepositoryFactory.New<IRepository<Customer>>().Get(myId);
RepositoryFactory.New<IWebServiceLogRepository>().GetOpenLogs(myId);

第1行: 因为这里的类型是IRepository,所以很清楚返回类型是什么,以及T类型对于基础IRepository是什么。

第2行: 这里的返回类型是IWebServiceLogRepository。在这里,您不需要指定实体类型,您的界面在逻辑上已经实现了IRepository。没有必要再次指定。

所以你的界面看起来像这样:

public interface IRepository<T>
{
T Get(object Id);
T Save(T object);
}

public interface IWebServiceLogRepository: IRepository<WebServiceLog>
{
List<WebServiceLog> GetOpenLogs(object Id);
}

现在我认为这样的实现和工厂代码会更简单,因为工厂只需知道一种类型。在第1行,类型是IRepository,在第2行,IWebServiceLogRepository。

尝试一下,尝试重写代码,只需找到实现这些类型的类并实例化它们。

最后,就MEF而言,你可以继续使用它,但Castle Windsor会让你的事情变得更加简单,因为它让你专注于你的架构和代码设计,而且它非常简单易用。您只在应用启动代码中引用Castle。您的其余代码只是使用依赖注入模式设计,这与框架无关。

如果其中一些不清楚,请告诉我您是否希望我使用您的存储库的实施代码更新此答案。

更新

这是解决实现的代码。你不使用Activator类让自己变得有点困难。

如果你使用Activator并且只使用一个Generic参数,就像我在下面的方法中所做的那样,你应该没问题。注意代码有点粗糙,但你明白了:

public static T GetThing<T>()
        {
            List<Type> assemblyTypes = AppDomain.CurrentDomain.GetAssemblies()
                                        .SelectMany(s => s.GetTypes()).ToList();

            Type interfaceType = typeof(T);

            if(interfaceType.IsGenericType)
            {
                var gens = interfaceType.GetGenericArguments();
                List<Type> narrowed = assemblyTypes.Where(p => p.IsGenericType && !p.IsInterface).ToList();
                var implementations = new List<Type>();
                narrowed.ForEach(t=>
                {
                    try
                    {
                        var imp = t.MakeGenericType(gens);
                        if(interfaceType.IsAssignableFrom(imp))
                        {
                            implementations.Add(imp);     
                        }  
                    }catch
                    {
                    }
                });

                return (T)Activator.CreateInstance(implementations.First());
            }
            else
            {
                List<Type> implementations = assemblyTypes.Where(p => interfaceType.IsAssignableFrom(p) && !p.IsInterface).ToList();

                return (T)Activator.CreateInstance(implementations.First());
            }

        }