Reputation: 3396
This is part of my code which I need help with:
// simple service locator
public class ServiceManager<TSvc> : IServiceManager<TSvc> where TSvc: class, IService
{
private Dictionary<object, TSvc> services;
public void RegisterService(TSvc service)
{
// omitted code here
this.services.Add(service.GetType(), service); // dictionary
}
public T GetService<T>() where T : TSvc
{
T result = default(T);
TSvc bufResult = null;
if (this.services.TryGetValue(typeof(T), out bufResult))
result = (T)bufResult;
return result;
}
public TSvc GetService(Type serviceType)
{
TSvc result = null;
this.services.TryGetValue(serviceType, out result);
return result;
}
}
Then my domain interfaces:
public interface IItem
{
string Name { get; set; }
}
public interface IRepository<TModel> where TModel : IItem
{
new IEnumerable<TModel> GetItems();
void InsertItem(TModel item);
void UpdateItem(TModel item);
void DeleteItem(TModel item);
}
public interface IService<TModel> where TModel : IItem
{
IRepository<TModel> Repository { get; }
}
Then some of my domain classes:
public class Book: IItem
{
public string Name { get; set; }
}
public class BookRepo: IRepository<Book>
{
new IEnumerable<Book> GetItems();
void InsertItem(Book item);
void UpdateItem(Book item);
void DeleteItem(Book item);
}
public class BookService: IService<Book>
{
IRepository<Book> IService<Book>.Repository { get { return this.Repository; } }
BookRepo Repository { get; set;}
}
Now, if I am interested to use 'BookService' and do something with it, I could get it from service locator like this:
public void DoSomething()
{
var bookService = serviceManager.GetService<BookService>();
bookService.Repository.Insert(new Book());
}
But the problem is that the type of the service is known only at runtime (eg. selection from combobox). So, how would DoSomething method look like?
public void DoSomething()
{
var typeOfService = combobox.SelectedValue.GetType(); // cbx of services
// ??? make use of serviceManager and typeOfService to get appropriate 'service'
service.Repository.Insert(/*new IITem here*/);
}
Also, I would like to know how would you connect IService
to IService<TModel>
... it could even get to the solution, but I have no idea how. My IService
interface is blank for the moment...
I would really appreciate your time. Please let me know if there is something unclear! Thank you!
Update: Based on your answers, I guess the reflection part could be involved (something like NSGaga pointed out), but still, without connecting IService
and IService<TModel>
I cannot achieve what I want. Who has any idea how to redesign this?
Upvotes: 5
Views: 201
Reputation: 43046
To call a generic method where the type is only known at runtime requires using reflection.
For example, in your last code sample, you rightly imply that you can't call GetService<BookService>
, but you are still adding new Book()
to the service. You would also have to use reflection to instantiate the new object, because, again, you don't know the type at compile time. So you would need to use reflection at least three times: Once to call the method that returns the service, once to create the new object, and once to call the insert method on the service.
You can isolate some of this complexity with dependency injection and inversion of control. For example, create a generic method like this:
void CreateNewObject<T>() where T : new()
{
var service = GetServiceFor<T>();
service.Repository.Insert(new T());
}
Now, you only need to use reflection once, to call that method, instead of three times.
Upvotes: 2
Reputation: 14302
Something like this should work (typing from my head, so you'd need to check the syntax details - but should give you the direction - or I'll add on later)
MethodInfo methodInfo = typeof(ServiceManager).GetMethod("GetService");
MethodInfo methodInfoGeneric = methodInfo.MakeGenericMethod(new[] { typeOfService });
methodInfoGeneric.Invoke(serviceManager, new object[] { });
Upvotes: 2