StructureMap and Proto.Actor in .NET Core 2 - structuremap

I have been using Proto.Actor and specifically the ActorFactory to spawn actors. To be able to use these features I need to add services.AddProtoActor() to the ConfigureServices method of my startup class.
However, now I want to transition to using StructureMap as my IoC container, but the two do not appear to play nicely together - when I add the following code from guides I have found online:
public IServiceProvider ConfigureIoC(IServiceCollection services)
{
// static class method that scans assemblies
IContainer container = IocContainer.SetupContainer();
container.Configure(config =>
{
config.Populate(services);
});
return container.GetInstance<IServiceProvider>();
}
When it tries to run config.Populate I get the following error:
System.ArgumentOutOfRangeException: Specified argument was out of the
range of valid values. Parameter name: EventStream must have at
least one public constructor to be plugged in by StructureMap
Does anyone have any ideas how to get the IActorFactory created correctly as a singleton in StructureMap (or have a workaround)?

In the end, using StructureMap removes the need I had for the ActorFactory itself. So instead of getting the actor's PID from the factory I have two lines:
var props = Actor.FromProducer(() => container.GetInstance<MyActorType>());
var pid = Actor.Spawn(props);

Related

Request instance within ConfigureServices

Within ConfigureServices I'm setting up a number of services (this works). In the same method I'm trying to configure a custom file provider (for Razor). It looks like this:
services.AddMvc()
.AddRazorOptions(options =>
{
options.FileProvider = new CustomFileProvider(???);
});
CustomFileProvider has a few dependencies (that are all configured), but how can I ask the DI to give me an instance of CustomFileProvider right after all the services have been configured?
From what I can see the DI only injects in constructors, but in my case I need a "please give me an instance of CustomFileProvider right here" option.
If you want to be able to resolve services from the container while configuring some options, you need to leverage the IConfigureOptions<TOption> infrastructure, which most of MVC uses to set up the option defaults. See RazorViewEngineOptionsSetup for an example.
First, add the services you need to the collection:
services.AddSingleton<IDependency1, Dependency1>();
services.AddSingleton<IDependency2, Dependency2>();
Then, implement the setup class:
public class CustomFileProviderRazorViewEngineOptionsSetup : ConfigureOptions<RazorViewEngineOptions>
{
public CustomFileProviderRazorViewEngineOptionsSetup(IServiceProvider serviceProvider)
: base(options => ConfigureRazor(options, serviceProvider))
{
}
private static void ConfigureRazor(RazorViewEngineOptions options, IServiceProvider serviceProvider)
{
// Alternative 1 - Resolve each service and new up the instance.
var dependency1 = serviceProvider.GetService<IDependency1>();
var dependency2 = serviceProvider.GetService<IDependency2>();
options.FileProviders.Add(new CustomFileProvider(dependency1, dependency2));
// Alternative 2 - Same as alternative 1, but with moar magic ;)
options.FileProviders.Add(ActivatorUtilities.CreateInstance<CustomFileProvider>(serviceProvider));
// Alternative 3 - Just resolve CustomFileProvider from the service provider.
// This requires it to be registered first, of course.
options.FileProviders.Add(serviceProvider.GetService<CustomFileProvider>());
}
}
This is resolved from the container, so it'll allow you to inject an IServiceProvider, which can again be used to resolve the services you need. You could optionally add the CustomFileProvider to the container and resolve that directly instead, like in alternative 3.
Finally, add the setup class to the service collection:
services.TryAddEnumerable(
ServiceDescriptor.Transient<
IConfigureOptions<RazorViewEngineOptions>,
CustomFileProviderRazorViewEngineOptionsSetup>());
This will add the setup to the options builder pipeline, which means it'll run with the other registered instances of IConfigureOptions<RazorViewEngineOptions> to setup the options object.

No default instance or named instance 'Default' for requested plugin type

I'm trying to avoid referencing the concrete type library in my main project, but I'm getting this error:
No default instance or named instance 'Default' for requested plugin type StackExchangeChatInterfaces.IClient
1.) Container.GetInstance(StackExchangeChatInterfaces.IClient ,{username=; password=; defaultRoomUrl=; System.Action`2[System.Object,System.Object]=System.Action`2[System.Object,System.Object]})
I've setup my container to scan for assemblies, like so:
var container = new Container(x =>
{
x.Scan(scan =>
{
scan.AssembliesFromApplicationBaseDirectory();
scan.ExcludeNamespace("StructureMap");
scan.WithDefaultConventions();
scan.AddAllTypesOf<IMessageHandlers>();
});
//x.For<IClient>().Use<Client>(); //GetInstance will work if this line is not commented out.
});
When I try to get an instance, I get the error, my code for getting an instance is here:
chatInterface = container
.With("username").EqualTo(username)
.With("password").EqualTo(password)
.With("defaultRoomUrl").EqualTo(roomUrl)
.With<Action<object, object>>(delegate(object sender, object messageWrapper)
{
string message = ((dynamic)messageWrapper).Message;
Console.WriteLine("");
Console.WriteLine(message);
foreach (var item in messageHandlers)
{
item.MessageHandler.Invoke(message, chatInterface);
}
}).GetInstance<IClient>();
If I explicitly map the concrete class to the interface, everything works hunky dory, but that means I need to reference the project that Client is in, which I don't want to do.
This is really interesting. Looks like default conventions are not able to register types with such constructor (tried on both versions 2.6.3 and 3+). I was only registered when only parameterless constructor was specified. Looking at sources of both versions it is really suspicious as it should be registered. Deeper dive into the code would be needed...
Anyway try using custom registration convention:
public class ClientConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
if (type.IsClass && !type.IsAbstract && !type.IsGenericType &&
type.GetInterfaces().Contains(typeof(IClient)))
{
registry.For(typeof(IClient)).Use(type);
}
}
}
Configure it like this:
var container = new Container(
c => c.Scan(
s =>
{
s.ExcludeNamespace("StructureMap");
s.WithDefaultConventions();
s.Convention<ClientConvention>();
s.AddAllTypesOf<IMessageHandlers>();
}));
and this should work just fine.
The default type scanning will not pick up concrete types whose constructor functions contain primitive arguments like strings, numbers, or dates. The thinking is that you'd effectively have to explicitly configure those inline dependencies anyway.
"but that means I need to reference the project that Client is in, which I don't want to do."
Does that actually matter? I think you're making things harder than they have to be by trying to eliminate the assembly reference.

Simple Injector manually registering controllers

I'm attempting to implement Dependency Injection into my architecture (MVC, DDD - Domain Model, Repository). And my architecture includes ASP.NET Identity 2.0.
At this stage, I don't want DI controlling any of the Identity 2.0 objects (UserAdminController, RolesAdminController...). I'd prefer the security objects outside of DI. At this stage, integrating the Identity objects in DI looks very difficult. I had a good look to see if someone has already done this, so I could read and learn how to do this. I couldn't find anything. (Found one post which came close, but no resolution).
Anyway, I've followed the Simple Injector MVC implementation (see standard code below), and trying many things, I believe the problem lies in me calling RegisterMvcControllers.
Correct me if I'm wrong, but this statement will pickup all controllers with their name post-fixed with "controller".
Question: How can I select which controllers get registered with Simple Injector? (Is this called manually registering?)
Any help would be greatly appreciated, as I've spent most of today trying to get my head around all this, and proceed to the next step, i.e. have DI implemented, and instantiating my objects.
...
...
... called from Application_Start()
// Create a Simple Injector container
var container = new Container();
// Configure the container
InitializeContainer(container);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
// Verify the container's configuration
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
private static void InitializeContainer(Container container)
{
container.Register<MyService1>();
container.Register<IMyRepositoryA, MyRepositoryA>();
// Trying to include Identity into Simple Injector - please ignore
container.Register<IUserStore<ApplicationUser>>(() => new UserStore<ApplicationUser>(new ApplicationDbContext()));
}
The RegisterMvcControllers has the following constraints on the types it registers:
The type must be public
The type must implement System.Web.Mvc.IController
The type must not be abstract
The type must not be a generic type definition
Its name must end with "Controller"
You can see what happens here in the source code.
The RegisterMvcControllers extension method calls into the SimpleInjectorMvcExtensions.GetControllerTypesToRegister method to get the list of controllers to register. You can call that method yourself to see what is registered as follows:
var registeredControllerTypes =
SimpleInjectorMvcExtensions.GetControllerTypesToRegister(
container, Assembly.GetExecutingAssembly())
So instead of calling RegisterMvcControllers you can register the controllers yourself by calling the GetControllerTypesToRegister method:
var registeredControllerTypes =
SimpleInjectorMvcExtensions.GetControllerTypesToRegister(
container, Assembly.GetExecutingAssembly());
foreach (var controllerType in registeredControllerTypes)
{
container.Register(controllerType, controllerType, Lifestyle.Transient);
}
This way you can filter out any controller you want to register manually:
var registeredControllerTypes =
SimpleInjectorMvcExtensions.GetControllerTypesToRegister(
container, Assembly.GetExecutingAssembly())
.Where(type => type.Name != "UserStore`1");
foreach (var controllerType in registeredControllerTypes)
{
container.Register(controllerType, controllerType, Lifestyle.Transient);
}
Another option is to override the registration:
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.Options.AllowOverridingRegistrations = true;
container.Register<IUserStore<ApplicationUser>>(
() => new UserStore<ApplicationUser>(new ApplicationDbContext()))
// Always set the option back to false ASAP to prevent configuration errors.
container.Options.AllowOverridingRegistrations = false;

Autofac error: The instance registration can support SingleInstance() sharing only

When I try to register my class with autofac I get the following error: "The instance registration 'GetAllDivisionsCommand' can support SingleInstance() sharing only".
I don't understand why I'm getting this error, but assume it's something to do with the class having static member variables used for caching as that's the only thing that's different about this class. I haven't had any trouble registering any other classes as either SingleInstance or InstancePerDependency.
Essentially, the class is used to retrieve a rarely changing list of divisions from the database, and caches the result. Each time the command is run, it first checks for changes on the database and then re-runs the query if changes are detected; if not, it returns the cached list.
So I am trying to register GetAllDivisionsCommand with Autofac as an IGetAllDivisionsCommand. IGetAllDivisionsCommand itself implements IInjectableCommand, which is just a marker interface, and ICachedListCommand. The concrete command class inherits from the abstract base class CachedListCommand which is where the static member variables live.
Does anyone know what would cause this error message? SingleInstance won't work for me as I can't keep reusing the same session.
Code:
Type commandType = typeof(IInjectedCommand);
Type aCommandType = typeof(GetAllDivisions);
var commands =
from t in aCommandType.Assembly.GetExportedTypes()
where t.Namespace == aCommandType.Namespace
&& t.IsClass
&& !t.IsAbstract
&& (commandType.IsAssignableFrom(t))
let iface = t.GetInterfaces().FirstOrDefault(x => "I" + t.Name == x.Name)
select new { Command = t, Interface = iface };
foreach (var cmd in commands)
{
builder.RegisterInstance(cmd.Command).As(cmd.Interface).InstancePerLifetimeScope();
}
RegisterInstace as the name implies is for registering instances not types.
What you need is RegisterType:
foreach (var cmd in commands)
{
builder.RegisterType(cmd.Command).As(cmd.Interface).InstancePerLifetimeScope();
}
And by the way with the Autofac scanning feature your registration code is roughly equivalent:
builder
.RegisterAssemblyTypes(aCommandType.Assembly)
.AssignableTo<IInjectedCommand>()
.InNamespace(aCommandType.Namespace)
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
In my case, I did want RegisterInstance because I actually have an instance in hand that I wanted to register.
I had
builder.RegisterInstance(myInstance).InstancePerDependency();
The documentation for InstancePerDependency reads:
Configure the component so that every dependent component or call to
Resolve() gets a new, unique instance (default.)
On closer inspection, it makes sense that registering an instance with "instance per dependency" is not possible, since it is not possible for Autofac to give back a new instance each time Resolve is called if there is in fact 1 instance registered.
So, in my case, the solution was this.
builder.RegisterInstance(myInstance).SingleInstance();
The Autofac exception could possibly have been worded more clearly to explain this problem.

Why getting a 202 in two equal setup structuremap code paths

In the C# language, using StructureMap 2.5.4, targeting .NET Framework 3.5 libraries.
I've taken the step to support multiple Profiles in a structure map DI setup, using ServiceLocator model with Bootstrapper activation. First setup was loading default registry, using the scanner.
Now I like to determine runtime what Registry configuration I like to use. Scanning and loading multiple assemblies with registries.
Seems it's not working for the actual implementation (Getting the 202, default instance not found), but a stripped test version does work. The following setup.
Two assemblies containing Registries and implementations
Scanning them in running AppDomain, providing the shared Interface, and requesting Creation Of Instance, using the interfaces in constructor (which get dealt with thanx to the profile on Invokation)
Working code sample below (same structure for other setup, but with more complex stuff, that get's a 202):
What type of couses are possible for a 202, specifically naming the System.Uri type, not being handles by a default type?? (uri makes no sense)
// let structure map create instance of class tester, that provides the registered
// interfaces in the registries to the constructor of tester.
public class Tester<TPOCO>
{
private ITestMe<TPOCO> _tester;
public Tester(ITestMe<TPOCO> some)
{
_tester = some;
}
public string Exec()
{
return _tester.Execute();
}
}
public static class Main {
public void ExecuteDIFunction() {
ObjectFactory.GetInstance<Tester<string>>().Exec();
}
}
public class ImplementedTestMe<TSome> : ITestMe<TSome>
{
public string Execute()
{
return "Special Execution";
}
}
public class RegistryForSpecial : Registry
{
public RegistryForSpecial()
{
CreateProfile("Special",
gc =>
{
gc.For(typeof(ITestMe<>)).UseConcreteType(typeof(ImplementedTestMe<>));
});
}
}
Background articles on Profiles I used.
How to setup named instances using StructureMap profiles?
http://devlicio.us/blogs/derik_whittaker/archive/2009/01/07/setting-up-profiles-in-structuremap-2-5.aspx
http://structuremap.sourceforge.net/RegistryDSL.htm
EDIT:
It seemed the missing interface was actually the one being determined runtime. So here is the next challange (and solved):
I provided a default object whenever StructureMap needs to create the object. Like:
x.ForRequestedType<IConnectionContext>()
.TheDefault.Is.Object(new WebServiceConnection());
This way I got rid of the 202 error, because now a real instance could be used whever structure map needed the type.
Next was the override on runtime. That did not work out at first using the ObjectFactory.Configure method. Instead I used the ObjectFactory.Inject method to overide the default instance. Works like a charm.
ObjectFactory.Inject(typeof(IConnectionContext), context);
Loving the community effort.
Error code 202 means a default instance could not be built for the requested type. Your test code is apparently not equal to your real code that fails. If you are getting an error about Uri, you likely have a dependency that requires a Uri in its constructor. It may not be the class you are asking for - it may be one of that classes dependendencies - or one of the dependencies dependencies... somewhere down the line someone is asking StructureMap to resolve a Uri, which it cannot do, without some help from you.

Resources