Reputation: 30904
I have the following interface and implementation:
public interface IRepository<T>
{
IList<T> GetAll();
}
internal class TrendDataRepository : IRepository<TrendData>
{
public IList<TrendData> GetAll()
{
//.. returns some specific data via Entity framework
}
}
I'm going to have multiple implementations that all return different data by Entity Framework. At some point I want to represent the user a list of classes that implement the IRepository interface. I do this with the following code. This works great for me.
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;
}
However, I would also like to create a factory method that given a specific string will return a concrete Repository type and allow me to call the 'GetAll' method on it. In pseudo-code:
someObject = Factory.CreateInstance("TrendData");
someObject.GetAll();
(I know this won't work because I have to specify a concrete type in the factory method).
I desire this functionality because I want to give a user the ability to bind a report to a specific datasource. This way they can start a new report where the datasource of the report is bound to (for example) the TrendDataRepository.GetAll() method.
However, maybe because the end of the world is getting near ;-) or it's Friday afternoon and I just can't think clearly any more, I don't know how to realise this.
Some pointers would be really welcomed.
Upvotes: 6
Views: 1378
Reputation: 144126
I'd suggest returning the collection of repository types instead of the names and just displaying the names in the UI:
public static IEnumerable<Type> GetAvailableRepositoryClasses()
{
return Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof (IRepository<>)));
}
Then when a user selects the source you can do:
object repository = Activator.CreateInstance(selectedType);
This method requires each repository to have a default constructor.
Activator.CreateInstance
return an object, and you can't cast it to your IRepository<T>
interface unless you know the generic type T
you're expecting. The best solution is probably to create a non-generic IRepository
interface which your repository classes also implement:
public interface IRepository
{
IList<object> GetAll();
}
Now you can cast your created repositories to IRepository
:
IRepository repository = (IRepository)Activator.CreateInstance(selectedType);
you might to create a repository base class which implements both:
public abstract class RepositoryBase<T> : IRepository<T>, IRepository
{
public abstract IList<T> GetAll();
IList<object> IRepository.GetAll()
{
return this.GetAll().Cast<object>().ToList();
}
}
Upvotes: 2