ищу правильный способ реализации моей родовой фабрики - PullRequest
1 голос
/ 18 апреля 2011

Я борюсь с реализацией объекта фабрики.Вот контекст:

У меня в проекте есть магазин.Чтобы читать / писать записи, я написал этот код в 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); -> стандартное получение
  • MyContext.GetRepository<WebServiceLog, IWebServiceLogRepository>().GetOpenLogs(myID);-> конкретная операция

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

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>>();
    }
}

I 'Буду признателен за помощь в разгадывании моего разума с этим довольно распространенным сценарием

1 Ответ

1 голос
/ 10 сентября 2011

Не уверен, правильно ли я вас понял, но я думаю, что вы, возможно, слишком усложняете вещи.Для начала, убедитесь, что вы разработали свой код независимо от какой-либо фабрики или Dependency Injection, фреймворка или структуры компоновки.

Для начала давайте посмотрим, как должен выглядеть ваш вызывающий код, это то, что вы сказали:

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());
            }

        }
...