Фабричный метод и дженерики
У меня есть следующий интерфейс и реализация:
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();
}
}