I want to register open generic interception, so I modified explample from https://github.com/dadhi/DryIoc/blob/master/docs/DryIoc.Docs/Interception.md
[TestFixture]
public class UnitTest3
{
public interface IFoo<T>
{
void Greet();
}
public class Foo<T> : IFoo<T>
{
public void Greet() { }
}
[Test]
public void Example()
{
var container = new Container();
container.Register(typeof(IFoo<>), typeof(Foo<>));
container.Register<FooLoggingInterceptor>(Reuse.Singleton);
container.Intercept<FooLoggingInterceptor>(typeof(IFoo<>));
var foo = container.Resolve<IFoo<int>>();
foo.Greet();
// examine that logging indeed was hooked up
var logger = container.Resolve<FooLoggingInterceptor>();
Assert.AreEqual("Invoking method: Greet", logger.LogLines[0]);
}
}
public class FooLoggingInterceptor : IInterceptor
{
public List<string> LogLines = new List<string>();
private void Log(string line) => LogLines.Add(line);
public void Intercept(IInvocation invocation)
{
Log($"Invoking method: {invocation.GetConcreteMethod().Name}");
invocation.Proceed();
}
}
public static class DryIocInterception
{
private static readonly DefaultProxyBuilder _proxyBuilder = new DefaultProxyBuilder();
public static void Intercept<TInterceptor>(this IRegistrator registrator, Type serviceType, object serviceKey = null)
where TInterceptor : class, IInterceptor
{
Type proxyType;
if (serviceType.IsInterface())
proxyType = _proxyBuilder.CreateInterfaceProxyTypeWithTargetInterface(
serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default); //Exception!!!
else if (serviceType.IsClass())
proxyType = _proxyBuilder.CreateClassProxyTypeWithTarget(
serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default);
else
throw new ArgumentException(
$"Intercepted service type {serviceType} is not a supported, cause it is nor a class nor an interface");
registrator.Register(serviceType, proxyType,
made: Made.Of(pt => pt.PublicConstructors().FindFirst(ctor => ctor.GetParameters().Length != 0),
Parameters.Of.Type<IInterceptor[]>(typeof(TInterceptor[]))),
setup: Setup.DecoratorOf(useDecorateeReuse: true, decorateeServiceKey: serviceKey));
}
}
But this throws exception:
Can not create proxy for type TestDryIoc.UnitTest3+IFoo`1 because type
TestDryIoc.UnitTest3+IFoo`1 is an open generic type.
Aparently Castle.Core doesn't support open generics. My other idea was to supply delegate, which will create the proxy class on resolve, when concrete generic param is known, but looks like DryIoc doesn't support open generic delegates...
Yeah, it is possible. So you need to find the closed type to pass it to the proxy? Wrap the code into the method and use the Made.Of which has access to the Request.ServiceType.
Related
I have RegisterMessageHandlers that register message handler per message name.
I want to automatically find and register all handlers through reflection. I can annotate each message with MessageAttribute (like shown below) and get the message name through reflection as well. The problem is when I want to instantiate a handler via Activator I have to provide all the dependencies in the constructor.
My solution is to register all instances with DI and pass IServiceProvider to MainManagerClass(IServiceProvider provider) and then use ActivatorUtilities.CreateInstance(handlerType) to instantiate each discovered handler through reflection.
But, then I read that there is DI and service locator which is antipattern and it's not very clear when one becomes the other.
So, I think I need a DI in order to accomplish what I want. Or do I?
public class MainManagerClass
{
private Dictionary<string, MessageHandler> _handlers;
private void RegisterMessageHandlers()
{
_messageHandlers["msg1"] = new MessageHandler1(new Service1());
_messageHandlers["msg2"] = new MessageHandler2(new Service1(), new Service2());
}
}
public class MessageHandler1 : MessageHandler<Message1>
{
public MessageHandler1(IService1 service){}
}
public class MessageHandler2 : MessageHandler<Message2>
{
public MessageHandler1(IService1 service1, IService2 service2){}
}
public abstract class MessageHandler<T> : MessageHandler
{
}
public abstract class MessageHandler
{
}
[Message("msg1")]
public class Message1
{
}
UPDATE
public class MainManagerClass
{
private Dictionary<string, MessageHandler> _handlers;
private readonly IServiceProvider _serviceProvider;
public MainManagerClass(IServiceProvider serviceProvider)
{
}
private void RegisterMessageHandlers()
{
var messageHandlers = Assembly.GetCallingAssembly()
.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(MessageHandler))).ToList();
foreach (var handler in messageHandlers)
{
var messageType = handler.BaseType.GenericTypeArguments[0];
var msgAttribute = messageType.CustomAttributes
.Where(t => t.AttributeType == typeof(MessageAttribute))
.FirstOrDefault();
if (msgAttribute == null)
throw new Exception($"Message name not defined for message type {messageType.Name}");
var msgName = msgAttribute.ConstructorArguments[0].Value.ToString();
_messageHandlers[msgName] = ActivatorUtilities.CreateInstance(_serviceProvider, handler) as MessageHandler;
}
}
}
internal class Program
{
static async Task Main(string[] args)
{
var host = CreateHostBuilder().Build();
var manager = new MainManagerClass(host.Services);
...
}
private static IHostBuilder CreateHostBuilder()
{
return Host.CreateDefaultBuilder()
.ConfigureServices((_, services) =>
{
services
.AddSingleton<IService1, Service1>()
.AddSingleton<IService2, Service2>()
.AddSingleton<IService3, Service3>()
;
});
}
}
If you don't want to add interface and keep abstract class as in your example - you can register your handlers like that:
builder.Services.AddTransient<MessageHandler<Message1>, MessageHandler1>();
builder.Services.AddTransient<MessageHandler<Message2>, MessageHandler2>();
And then if you inject MessageHandler<Message1> in your controller for instance, MessageHandler1 will be resolved as well as other dependencies (IService1 in your case).
I think I still don't get your question but I hope this will be helpfull for you. Anyway, I followed Jaroslav's answer and your comment to create MessageHandlerFactory object then I changed your code a bit to cover both scenarios but you need the message type to resolve its handler from the service provider plus, If you want to register all handlers automatically, you should use reflection at startup, find all objects that inherit from IMessageHandler<T>, and register them to DI or use packages like Scrutor.
public class MessageHandlerFactory
{
private IServiceProvider _serviceProvider;
public IMessageHandler<T> ResolveHandler<T>()
{
return _serviceProvider.GetRequiredService<IMessageHandler<T>>();
}
}
public class MessageHandler1 : IMessageHandler<Message1>
{
public MessageHandler1(IService1 service){}
}
public class MessageHandler2 : IMessageHandler<Message2>
{
public MessageHandler2(IService1 service1, IService2 service2){}
}
public interface IMessageHandler<T>
{
}
public class Message1
{
}
public class Message2
{
}
builder.Services.AddScoped<IMessageHandler<Message1>, MessageHandler1>();
builder.Services.AddScoped<IMessageHandler<Message2>, MessageHandler2>();
I need to be able to pass a connection string into some of my service implementations. I am doing this in the constructor. The connection string is configurable by user will be added the ClaimsPrincipal as a Claim.
All fine so far.
Unfortunately, I also want to be able to use the dependency injection features in ASP.NET Core to the fullest and resolve the service implementation though DI.
I have a POC implmentation:
public interface IRootService
{
INestedService NestedService { get; set; }
void DoSomething();
}
public class RootService : IRootService
{
public INestedService NestedService { get; set; }
public RootService(INestedService nestedService)
{
NestedService = nestedService;
}
public void DoSomething()
{
// implement
}
}
public interface INestedService
{
string ConnectionString { get; set; }
void DoSomethingElse();
}
public class NestedService : INestedService
{
public string ConnectionString { get; set; }
public NestedService(string connectionString)
{
ConnectionString = connectionString;
}
public void DoSomethingElse()
{
// implement
}
}
These services have been registered during startup and INestedService has been added the constructor of a controller.
public HomeController(INestedService nestedService)
{
NestedService = nestedService;
}
As expected, I get the error Unable to resolve service for type 'System.String' while attempting to activate 'Test.Dependency.Services.NestedService'.
What are my options here?
To pass a runtime parameter not known at the start of the application, you have to use the factory pattern. You have two options here:
factory class (similar to how IHttpClientFactory is implemented)
public class RootService : IRootService
{
public RootService(INestedService nested, IOtherService other)
{
// ...
}
}
public class RootServiceFactory : IRootServiceFactory
{
// in case you need other dependencies, that can be resolved by DI
private readonly IServiceProvider services;
public RootServiceFactory(IServiceProvider services)
{
this.services = services;
}
public IRootService CreateInstance(string connectionString)
{
// instantiate service that needs runtime parameter
var nestedService = new NestedService(connectionString);
// note that in this example, RootService also has a dependency on
// IOtherService - ActivatorUtilities.CreateInstance will automagically
// resolve that dependency, and any others not explicitly provided, from
// the specified IServiceProvider
return ActivatorUtilities.CreateInstance<RootService>(services,
new object[] { nestedService, });
}
}
and inject IRootServiceFactory instead of your IRootService
IRootService rootService = rootServiceFactory.CreateInstance(connectionString);
factory method
services.AddTransient<Func<string,INestedService>>((provider) =>
{
return new Func<string,INestedService>(
(connectionString) => new NestedService(connectionString)
);
});
and inject the factory method into your service instead of INestedService
public class RootService : IRootService
{
public INestedService NestedService { get; set; }
public RootService(Func<string,INestedService> nestedServiceFactory)
{
NestedService = nestedServiceFactory("ConnectionStringHere");
}
public void DoSomething()
{
// implement
}
}
or resolve it per call
public class RootService : IRootService
{
public Func<string,INestedService> NestedServiceFactory { get; set; }
public RootService(Func<string,INestedService> nestedServiceFactory)
{
NestedServiceFactory = nestedServiceFactory;
}
public void DoSomething(string connectionString)
{
var nestedService = nestedServiceFactory(connectionString);
// implement
}
}
Simple configuration
public void ConfigureServices(IServiceCollection services)
{
// Choose Scope, Singleton or Transient method
services.AddSingleton<IRootService, RootService>();
services.AddSingleton<INestedService, NestedService>(serviceProvider=>
{
return new NestedService("someConnectionString");
});
}
With appSettings.json
If you decide to hide your connection string inside appSettings.json, e.g:
"Data": {
"ConnectionString": "someConnectionString"
}
Then provided that you've loaded your appSettings.json in the ConfigurationBuilder (usually located in the constructor of the Startup class), then your ConfigureServices would look like this:
public void ConfigureServices(IServiceCollection services)
{
// Choose Scope, Singleton or Transient method
services.AddSingleton<IRootService, RootService>();
services.AddSingleton<INestedService, NestedService>(serviceProvider=>
{
var connectionString = Configuration["Data:ConnectionString"];
return new NestedService(connectionString);
});
}
With extension methods
namespace Microsoft.Extensions.DependencyInjection
{
public static class RootServiceExtensions //you can pick a better name
{
//again pick a better name
public static IServiceCollection AddRootServices(this IServiceCollection services, string connectionString)
{
// Choose Scope, Singleton or Transient method
services.AddSingleton<IRootService, RootService>();
services.AddSingleton<INestedService, NestedService>(_ =>
new NestedService(connectionString));
}
}
}
Then your ConfigureServices method would look like this
public void ConfigureServices(IServiceCollection services)
{
var connectionString = Configuration["Data:ConnectionString"];
services.AddRootServices(connectionString);
}
With options builder
Should you need more parameters, you can go a step further and create an options class which you pass to RootService's constructor. If it becomes complex, you can use the Builder pattern.
I devised this little pattern to help me resolve objects that require runtime parameters ,but also have dependencies which the DI container is able to resolve - I implemented this using the MS DI Container for a WPF App.
I already had a Service Locator (yes I know its a code smell - but I attempt to resolve that by the end of the example) that I used in specific scenarios to get access to objects in the DIC:
public interface IServiceFactory
{
T Get<T>();
}
Its implementation takes a func<> in the constructor to decouple the fact it relies on MS DI.
public class ServiceFactory : IServiceFactory
{
private readonly Func<Type, object> factory;
public ServiceFactory(Func<Type, object> factory)
{
this.factory = factory;
}
// Get an object of type T where T is usually an interface
public T Get<T>()
{
return (T)factory(typeof(T));
}
}
This was created in the composition root like so:
services.AddSingleton<IServiceFactory>(provider => new ServiceFactory(provider.GetService));
This pattern was extended to not only 'Get' objects of type T, but 'Create' objects of type T with parameters P:
public interface IServiceFactory
{
T Get<T>();
T Create<T>(params object[] p);
}
The implementation took another func<> to decouple the creation mechanism:
public class ServiceFactory : IServiceFactory
{
private readonly Func<Type, object> factory;
private readonly Func<Type, object[], object> creator;
public ServiceFactory(Func<Type, object> factory, Func<Type, object[], object> creator)
{
this.factory = factory;
this.creator = creator;
}
// Get an object of type T where T is usually an interface
public T Get<T>()
{
return (T)factory(typeof(T));
}
// Create (an obviously transient) object of type T, with runtime parameters 'p'
public T Create<T>(params object[] p)
{
IService<T> lookup = Get<IService<T>>();
return (T)creator(lookup.Type(), p);
}
}
The creation mechanism for the MS DI container is in the ActivatorUtilities extensions, here's the updated composition root:
services.AddSingleton<IServiceFactory>(
provider => new ServiceFactory(
provider.GetService,
(T, P) => ActivatorUtilities.CreateInstance(provider, T, P)));
Now that we can create objects the problem becomes we have no way of determining the type of object we need without the DI container actually creating an object of that type, which is where the IService interface comes in:
public interface IService<I>
{
// Returns mapped type for this I
Type Type();
}
This is used to determine what type we are trying to create, without actually creating the type, its implementation is:
public class Service<I, T> : IService<I>
{
public Type Type()
{
return typeof(T);
}
}
So to pull it all together, in your composition root you can have objects that don't have runtime parameters which can be resolved by 'Get' and ones which do resolved by 'Create' e.g.:
services.AddSingleton<ICategorySelectionVM, CategorySelectionVM>();
services.AddSingleton<IService<ISubCategorySelectionVM>, Service<ISubCategorySelectionVM, SubCategorySelectionVM>>();
services.AddSingleton<ILogger, Logger>();
The CategorySelectionVM has only dependencies that can be resolved via the DIC:
public CategorySelectionVM(ILogger logger) // constructor
And this can be created by anyone with a dependency on the service factory like:
public MainWindowVM(IServiceFactory serviceFactory) // constructor
{
}
private void OnHomeEvent()
{
CurrentView = serviceFactory.Get<ICategorySelectionVM>();
}
Where as the SubCategorySelectionVM has both dependencies that the DIC can resolve, and dependencies only known at runtime:
public SubCategorySelectionVM(ILogger logger, Category c) // constructor
And these can be created like so:
private void OnCategorySelectedEvent(Category category)
{
CurrentView = serviceFactory.Create<ISubCategorySelectionVM>(category);
}
Update : I just wanted to add a little enhancement which avoided using the service factory like a service locator, so I created a generic service factory which could only resolve objects of type B:
public interface IServiceFactory<B>
{
T Get<T>() where T : B;
T Create<T>(params object[] p) where T : B;
}
The implementation of this depends on the original service factory which could resolve objects of any type:
public class ServiceFactory<B> : IServiceFactory<B>
{
private readonly IServiceFactory serviceFactory;
public ServiceFactory(IServiceFactory serviceFactory)
{
this.serviceFactory = serviceFactory;
}
public T Get<T>() where T : B
{
return serviceFactory.Get<T>();
}
public T Create<T>(params object[] p) where T : B
{
return serviceFactory.Create<T>(p);
}
}
The composition root adds the original service factory for all the generic typed factories to depend on, and any of the typed factories:
services.AddSingleton<IServiceFactory>(provider => new ServiceFactory(provider.GetService, (T, P) => ActivatorUtilities.CreateInstance(provider, T, P)));
services.AddSingleton<IServiceFactory<BaseVM>, ServiceFactory<BaseVM>>();
Now our main view model can be restricted to creating only objects that derive from BaseVM:
public MainWindowVM(IServiceFactory<BaseVM> viewModelFactory)
{
this.viewModelFactory = viewModelFactory;
}
private void OnCategorySelectedEvent(Category category)
{
CurrentView = viewModelFactory.Create<SubCategorySelectionVM>(category);
}
private void OnHomeEvent()
{
CurrentView = viewModelFactory.Get<CategorySelectionVM>();
}
I know this is a bit old but thought i'd give my input since there is a easier way to do this in my opinion. This doesn't cover all the cases as shown in other posts. But this is a easy way of doing it.
public class MySingleton {
public MySingleton(string s, int i, bool b){
...
}
}
No lets create a service extention class to add easier and keep it neet
public static class ServiceCollectionExtentions
{
public static IServiceCollection RegisterSingleton(this IServiceCollection services, string s, int i, bool b) =>
services.AddSingleton(new MySingleton(s, i, b));
}
Now to call it from startup
services.RegisterSingleton("s", 1, true);
IMHO, follow the options pattern. Define a strong type to hold your connection string, then an IConfigureOptions<T> to configure it from your user claim.
public class ConnectionString {
public string Value { get; set; }
}
public class ConfigureConnection : IConfigureOptions<ConnectionString> {
private readonly IHttpContextAccessor accessor;
public ConfigureConnection (IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public void Configure(ConnectionString config) {
config.Value = accessor.HttpContext.User ...
}
}
public class NestedService {
...
public NestedService(IOptions<ConnectionString> connection) {
ConnectionString = connection.Value.Value;
}
...
}
Further to #Tseng's extremely helpful answer, I found I could also adapt it to use delegates:
public delegate INestedService CreateNestedService(string connectionString);
services.AddTransient((provider) => new CreateNestedService(
(connectionString) => new NestedService(connectionString)
));
Implemented in RootService in the same way #Tseng suggested:
public class RootService : IRootService
{
public INestedService NestedService { get; set; }
public RootService(CreateNestedService createNestedService)
{
NestedService = createNestedService("ConnectionStringHere");
}
public void DoSomething()
{
// implement
}
}
I prefer this approach for cases where I need an instance of a factory in a class, as it means I can have a property of type CreateNestedService rather than Func<string, INestedService>.
This question already has answers here:
covariance in c#
(5 answers)
Closed 9 years ago.
In IMyMessage.cs
public interface IMyMessage
{
}
In IMyMessageReceiver.cs
public interface IMyMessageReceiver<T> where T: IMyMessage
{
void HandleMessage(T message);
void Subscribe();
}
In MyMessagePublisher.cs
public static class MyMessagePublisher
{
private static Dictionary<Type, List<IMyMessageReceiver<IMyMessage>>> _subscribers;
static MyMessagePublisher
{
_subscribers = new Dictionary<Type, List<IMyMessageReceiver<IMyMessage>>>();
}
public static function Subscribe<T>(IMyMessageReceiver<T> receiver) where T: IMyMessage
{
Type messageType = typeof (T);
List<IMyMessageReceiver<IMyMessage>> listeners;
if(!_subscribers.TryGetValue(messageType, out listeners))
{
// no list found, so create it
List<IMyMessageReceiver<T>> newListeners = new List<IMyMessageReceiver<T>>();
// ERROR HERE: Can't convert List<IMyMessageReceiver<T>> to List<IMyMessageReceiver<IMyMessage>>
_subscribers.add(messageType, newListeners);
}
// I would then find the right list and add the receiver it to it but haven't got this far
}
}
So my hope was to use a bunch of 'IMyMessages' and 'IMyMessageReceivers' to pass messages around. I did a hard coded approach earlier but got sick of 100 different publish/subscrive function names so I figured I'd wrap it all nicely in generics.
My problem is that I can't get the code to work when using generics. Even though I specify the Type T will be of IMyMessage, I cannot use T anywhere where IMyMessage is expected. Maybe I'm just used to base/extended classes as it would work fine there. I've tried various approaches from casting, to being really generic, yet I always run in to the same issue.
OK here's how I can see it working. Since you're trying to use covariance in a way that is not supported, you'll need to avoid using generics in a few spots. But doing so won't lose any type-safety.
Create a non-generic IMessageReceiver interface so that the types that can't use the generic parameter can use this instead:
public interface IMyMessageReceiver
{
void HandleMessage(IMyMessage message);
void Subscribe();
}
public interface IMyMessageReceiver<in T> : IMyMessageReceiver
where T : IMyMessage
{
void HandleMessage(T message);
}
You can create a base class to simplify things if you'd like:
public abstract class MyMessageReceiverBase<T> : IMyMessageReceiver<T>
where T : IMyMessage
{
public abstract void HandleMessage(T message);
public void HandleMessage(IMyMessage message)
{
if (!(message is T))
throw new InvalidOperationException();
HandleMessage((T)message);
}
public abstract void Subscribe();
}
Then you can change IMyMessageListeners to use the non-generic version, since it doesn't really need the generic type anyways:
public interface IMyMessageListeners
{
void Add(IMyMessageReceiver receiver);
// I added this since I think this is how you're going to use it
void Send(IMyMessage message);
}
The concrete of this class looks like this:
public class MyMessageListeners : IMyMessageListeners
{
readonly List<IMyMessageReceiver> _list = new List<IMyMessageReceiver>();
public void Add(IMyMessageReceiver receiver)
{
_list.Add(receiver);
}
public void Send(IMyMessage message)
{
foreach (var listener in _list)
listener.HandleMessage(message);
}
}
Then (finally), your static class will look like this:
public static class MyMessagePublisher
{
static readonly Dictionary<Type, IMyMessageListeners> _subscribers = new Dictionary<Type, IMyMessageListeners>();
// I added this too, since I think this is how you intend to use it
public static void Publish<T>(T message) where T : IMyMessage
{
Type messageType = typeof(T);
IMyMessageListeners listeners;
if (_subscribers.TryGetValue(messageType, out listeners))
listeners.Send(message);
}
public static void Subscribe<T>(IMyMessageReceiver<T> receiver) where T : IMyMessage
{
Type messageType = typeof(T);
IMyMessageListeners listeners;
if (!_subscribers.TryGetValue(messageType, out listeners))
{
// no list found, so create it
listeners = new MyMessageListeners();
_subscribers.Add(messageType, listeners);
}
listeners.Add(receiver);
}
}
And you can use your static class like so:
MyMessagePublisher.Subscribe(new FooMessageReceiver());
MyMessagePublisher.Publish(new FooMessage());
Generics do not support covariance. Therfore IMyMessageReceiver T cannot be cast into IMyMessageReceiver IMyMessage even if T implements IMyMessage. That is why you get your error.
I don't think you should use generics in IMyMessageReceiver interface. I am not sure about what you are trying to achieve but maybe something like that would do the trick:
public interface IMyMessageReceiver
{
void HandleMessage(IMyMessage message);
void Subscribe();
}
public class MyMessageReceiver<T> : IMyMessageReceiver where T: IMyMessage
{
void IMyMessageReceiver.HandleMessage(IMyMessage message)
{
HandleMessage(message as T);
}
public void HandleMessage(T message) {...}
public void Subscribe() {...}
}
Given that you know the types will always be IMyMessageReceiver<T> you have emulate the behaviour by storing everthing as an object and casting:
private static Dictionary<Type, List<object>> _subscribers;
public static function Subscribe<T>(IMyMessageReceiver<T> receiver) where T: IMyMessage
{
Type messageType = typeof (T);
List<object> listeners;
if(!_subscribers.TryGetValue(messageType, out listeners))
{
// no list found, so create it
List<object> newListeners = new List<object>();
newListeners.Add(receiver)
_subscribers.add(messageType, newListeners);
}
var messageReceivers = listeners.Cast<IMyMessageReceiver<T>>();
}
Since you know the list in the dictionary will always be of a given type you can cast them with confidence!
In this case KeyedByTypeCollection<T> is appropriate:
public interface IMyMessage
{
}
public interface IMyMessageReceiver<T> where T : IMyMessage
{
void HandleMessage(T message);
void Subscribe();
}
public static class MyMessagePublisher
{
private static readonly KeyedByTypeCollection<IList> Subscribers;
static MyMessagePublisher()
{
Subscribers = new KeyedByTypeCollection<IList>();
}
public static void Subscribe<T>(IMyMessageReceiver<T> receiver) where T : IMyMessage
{
List<IMyMessageReceiver<T>> listeners = Subscribers.Find<List<IMyMessageReceiver<T>>>();
if (listeners == null)
{
listeners = new List<IMyMessageReceiver<T>>();
Subscribers.Add(listeners);
}
// Now you can use the listeners list
listeners.Add(receiver);
}
}
I need to combine multiple interfaces a runtime to create a new type. For example I might have the following interfaces:
public interface IA{
}
public interface IB{
}
At runtime I want to be able to generate another interface so that in the following sudo code works:
Type newInterface = generator.Combine(typeof(IA), typeof(IB));
var instance = generator.CreateInstance(newInterface);
Assert.IsTrue(instance is IA);
Assert.IsTrue(instance is IB);
Is there a way to do this in .Net C#?
It is possible because of power of Castle Dynamic Proxy
public interface A
{
void DoA();
}
public interface B
{
void DoB();
}
public class IInterceptorX : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine(invocation.Method.Name + " is beign invoked");
}
}
class Program
{
static void Main(string[] args)
{
var generator = new ProxyGenerator();
dynamic newObject = generator.CreateInterfaceProxyWithoutTarget(typeof(A), new Type[] { typeof(B) }, new IInterceptorX());
Console.WriteLine(newObject is A); // True
Console.WriteLine(newObject is B); // True
newObject.DoA(); // DoA is being invoked
}
}
I have a generic interface, that takes in two generic types. I want to decorate all versions returned, but since I don't know the type when calling EnrichWith, it obviously doesn't compile. I've tried using the EnrichWith overload that passes in the context, thinking maybe I could grab the generic types passed in and call Activator.CreateInstance, but the context doesn't have any useful information on it when debugging and inspecting it.
Here's what I have so far. This is my generic interface:
public interface IServiceOperation<in TRequest, out TResponse> where TResponse : ServiceResult, new()
{
TResponse PerformService(TRequest validatedRequest);
}
Here's a sample implementation:
public class SignUpService : IServiceOperation<SignUpRequest, SignUpResult>
{
private readonly IUserRepository _userRepo;
public SignUpService(IUserRepository userRepo)
{
_userRepo = userRepo;
}
public SignUpResult PerformService(SignUpRequest validatedRequest)
{
var user = Mapper.Map<User>(validatedRequest);
user.MarkAsLoggedIn();
user.ChangePassword(validatedRequest.UnhashedPassword);
using(var transaction = _userRepo.BeginTransaction())
{
_userRepo.Save(user);
transaction.Commit();
}
return new SignUpResult();
}
}
Here is my decorator, that takes in another service as well:
public class ValidateServiceDecorator<TRequest, TResponse> : IServiceOperation<TRequest, TResponse> where TResponse : ServiceResult, new()
{
private readonly IServiceOperation<TRequest, TResponse> _serviceOperation;
private readonly IValidationService _validationService;
public ValidateServiceDecorator(IServiceOperation<TRequest, TResponse> serviceOperation,
IValidationService validationService)
{
_serviceOperation = serviceOperation;
_validationService = validationService;
}
public TResponse PerformService(TRequest request)
{
var response = new TResponse();
var validationResult = _validationService.Validate(request);
if (!validationResult.IsValid)
{
response.ValidationErrors = validationResult.ValidationErrors;
return response;
}
return _serviceOperation.PerformService(request);
}
Lastly, here is how far I've gotten on my container. This obviously doesn't compile, but the EnrichWith line shows what I'm trying to achieve:
public class StructureMapServiceScanner : Registry
{
public StructureMapServiceScanner()
{
Scan(scanner =>
{
scanner.AssemblyContainingType(typeof (IServiceOperation<,>));
scanner.ConnectImplementationsToTypesClosing(typeof (IServiceOperation<,>));
});
For(typeof (IServiceOperation<,>))
.EnrichWith((ioc, original) => new ValidateServiceDecorator(original, ioc.GetInstance<IValidationService>()));
}
}
And just because this question needed a little more code, here's my test that I'm trying to get to pass:
[TestClass]
public class StructureMapServiceScannerSpecs
{
[TestMethod]
public void Test()
{
ObjectFactory.Configure(cfg =>
{
cfg.AddRegistry<StructureMapServiceScanner>();
cfg.For<IUserRepository>().Use(new Mock<IUserRepository>().Object);
cfg.For<IValidationService>().Use(new Mock<IValidationService>().Object);
});
var service = ObjectFactory.GetInstance<IServiceOperation<SignUpRequest, SignUpResult>>();
service.ShouldNotBeNull();
service.ShouldBeType<ValidateServiceDecorator<SignUpRequest, SignUpResult>>();
}
}
I feel like this is something that should be simple, and I'm really missing something with how to use StructureMap. I could create type-specific versions for all combinations of Request and Response types, but obviously that's not desirable. So what am I missing?
Was able to figure it out, eventually. I created a RegistrationConvention:
public class ServiceRegistrationConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
var interfacesImplemented = type.GetInterfaces();
foreach (var interfaceImplemented in interfacesImplemented)
{
if (interfaceImplemented.IsGenericType && interfaceImplemented.GetGenericTypeDefinition() == typeof(IServiceOperation<,>))
{
var genericParameters = interfaceImplemented.GetGenericArguments();
var closedValidatorType = typeof(ValidateServiceDecorator<,>).MakeGenericType(genericParameters);
registry.For(interfaceImplemented)
.EnrichWith((context, original) => Activator.CreateInstance(closedValidatorType, original,
context.GetInstance<IValidationService>()));
}
}
}
}
Here's an approach that still leverages StructureMap's IoC capabilities, allowing additional services to be injected easily into your decorator. It's not perfect since it assumes you are using the primary container and not a child container, but it will probably work for most scenarios.
public class ServiceRegistrationConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
var handlerInterfaces = (from t in type.GetInterfaces()
where t.IsGenericType &&
t.GetGenericTypeDefinition() == typeof (IHandle<,>)
select t);
foreach (var handler in handlerInterfaces)
{
var decoratorType = typeof (ValidationDecorator<,>).MakeGenericType(handler.GetGenericArguments());
registry.For(handler)
.EnrichWith((ctx, orig) => ObjectFactory.With(handler, orig).GetInstance(decoratorType));
}
}
}
Ideally, StructureMap's IContext should expose the With method just like IContainer does. Without that, there's not really a great solution to this problem.