Фабричный метод и дженерики

У меня есть следующий интерфейс и реализация:

public interface IRepository<T>
{
    IList<T> GetAll();
}

internal class TrendDataRepository : IRepository<TrendData>
{
    public IList<TrendData> GetAll()
    {
        //.. returns some specific data via Entity framework
    }
}

Я собираюсь иметь несколько реализаций, которые все возвращают разные данные Entity Framework. В какой-то момент я хочу представить пользователю список классов, которые реализуют интерфейс IRepository. Я делаю это с помощью следующего кода. Это прекрасно работает для меня.

    public static IEnumerable<string> GetAvailableRepositoryClasses()
    {
        var repositories = from t in Assembly.GetExecutingAssembly().GetTypes()
                           where t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof (IRepository<>))
                           select t.Name;

        return repositories;
    }

Тем не менее, я также хотел бы создать фабричный метод, который с заданной конкретной строкой вернет конкретный тип репозитория и позволит мне вызывать метод GetAll для него. В псевдокоде:

someObject = Factory.CreateInstance("TrendData");
someObject.GetAll();

(Я знаю, что это не сработает, потому что я должен указать конкретный тип в фабричном методе).

Мне нужна эта функциональность, потому что я хочу дать пользователю возможность привязать отчет к определенному источнику данных. Таким образом, они могут начать новый отчет, где источник данных отчета связан (например, с) методом TrendDataRepository.GetAll().

Однако, может быть, потому, что конец света приближается;-) или сегодня пятница, и я просто больше не могу думать ясно, я не знаю, как это реализовать.

Некоторые указатели будут действительно приветствоваться.

1 ответ

Решение

Я бы предложил вернуть коллекцию типов репозитория вместо имен и просто отобразить имена в пользовательском интерфейсе:

public static IEnumerable<Type> GetAvailableRepositoryClasses()
{
    return Assembly.GetExecutingAssembly().GetTypes()
        .Where(t => t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof (IRepository<>)));
}

Затем, когда пользователь выбирает источник, вы можете сделать:

object repository = Activator.CreateInstance(selectedType);

Этот метод требует, чтобы у каждого хранилища был конструктор по умолчанию.

Activator.CreateInstance вернуть объект, и вы не можете привести его к IRepository<T> интерфейс, если вы не знаете общий тип T ты ожидаешь. Лучшее решение, вероятно, заключается в создании неуниверсального IRepository интерфейс, который ваши классы репозитория также реализуют:

public interface IRepository
{
    IList<object> GetAll();
}

Теперь вы можете привести свои созданные репозитории в IRepository:

IRepository repository = (IRepository)Activator.CreateInstance(selectedType);

Вы можете создать базовый класс хранилища, который реализует оба:

public abstract class RepositoryBase<T> : IRepository<T>, IRepository
{
    public abstract IList<T> GetAll();
    IList<object> IRepository.GetAll()
    {
        return this.GetAll().Cast<object>().ToList();
    }
}
Другие вопросы по тегам