I have a C# solution that includes multiple WebAPI projects. One of these projects, let's call it Project A, already uses SimpleInjector successfully. I'm trying to add SimpleInjector to another of these WebAPI projects, Project B, but I'm facing a problem.
I'm trying to create a second container at Project B as I did in Project A, but when I do this and try to build the solution, there is and exception in Project A, which is built after Project B, at the container.Verify() method. It tells me that a interface that is located at Project B (IUserService) is not properly registered at Project A, but Project A doesn't use this interface.
In Project B at Global.asax.cs I have this configuration:
/* Dependency Injection */
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
container.Register<IUserService>(() => { return new UserService(); }, Lifestyle.Scoped);
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
container.Verify();
GlobalConfiguration.Configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
In Project A, I have this configuration:
/* Dependency Injection */
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
container.Register<ILog>(() => { return LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); }, Lifestyle.Scoped);
container.Register<IFundRepository>(() => { return new IporangaFundRepository(dbConnectorMiddle); }, Lifestyle.Scoped);
container.Register<ITradeRepository>(() => { return new IporangaTradeRepository(dbConnectorMiddle, middleReadClient); }, Lifestyle.Scoped);
container.Register<ITradeManager, TradeManager>(Lifestyle.Scoped);
container.Register<ITradeService>(() => new TradeService(container.GetInstance<ITradeManager>()),Lifestyle.Scoped);
container.Register<ISimulationService>(() => new SimulationService(container.GetInstance<ITradeService>()), Lifestyle.Scoped);
container.Register<IBookerService>(() => new BookerService(container.GetInstance<ITradeService>(), container.GetInstance<ISimulationService>()), Lifestyle.Scoped);
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
container.Verify();
GlobalConfiguration.Configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
The error message:
System.InvalidOperationException: 'The configuration is invalid.
Creating the instance for type UserController failed. The constructor
of type UserController contains the parameter with name 'userService'
and type IUserService that is not registered. Please ensure
IUserService is registered, or change the constructor of
UserController.'
The following is not an answer to your question, but rather a suggestion how to improve and simplify the registrations for Project A.
Instead of using the posted code, I'd suggest using the following code to wire up the Container instance of Project A:
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
// For Log4NetAdapter<T>, please see: https://stackoverflow.com/a/25113659
container.RegisterConditional(typeof(ILog),
c => typeof(Log4NetAdapter<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
container.RegisterInstance(dbConnectorMiddle);
container.RegisterInstance(middleReadClient);
container.Register<IFundRepository, IporangaFundRepository>(Lifestyle.Scoped);
container.Register<ITradeRepository, IporangaTradeRepository(Lifestyle.Scoped);
container.Register<ITradeManager, TradeManager>(Lifestyle.Scoped);
container.Register<ITradeService, TradeService>(Lifestyle.Scoped);
container.Register<ISimulationService, SimulationService>(Lifestyle.Scoped);
container.Register<IBookerService, BookerService(Lifestyle.Scoped);
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
container.Verify();
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
This is better because:
Registrations are simplified because they only specify the mapping between the abstraction (e.g. ITradeService) and the implementation (e.g. TradeService), while letting Simple Injector Auto-Wire the types by inspecting the constructors' dependencies.
Not only are the registrations simplified, but now Simple Injector is aware of the structure of the complete dependency graph and can therefore effectively do verification on your behalf. This will, for instance, allow Simple Injector to find any Lifestyle Mismatches.
A conditional registration is used for the ILog abstraction. This allows Simple Injector to inject an ILog implementation specific to the consuming type. This allows your logging library to log information about the originating class. This is something that didn't work in your registration, where it would always inject a logger with a type that contains your registrations.
RegisterWebApiControllers uses reflection under the covers to search for implementations of ApiController.
I guess, based upon the error you get, project B is referenced by project A and the call to container.RegisterWebApiControllers in project A finds and registers the controllers of project B also.
Now when .Verify() is called it will scan the constructors of the ApiControllers in project B, finds a dependency on IUserService and breaks because this registration is actually missing in project A.
The integration package contains another overload for RegisterWebApiControllers which takes an array of assemblies that must be scanned for ApiControllers instead of scanning through all referenced assemblies.
Assuming the assembly of project A contains all ApiControllers that need to be registered this overload can be used in project A like:
container.RegisterWebApiControllers(GlobalConfiguration.Configuration,
new[] {typeof(MyApiControllerInProjectA).Assembly});
Related
I have a project created using the clean architecture template.
If I want a domain event be raised when a new project is created, where do I add that?
If I have to raise an event whenever a new item be added to a project, I can accomplish that in the Project entity as shown here.
Similarly for MarkCompletion of a ToDoItem as done here.
But its not clear where do I put the code to raise an event when a new Project is created?
One option is doing something like the following in Create End Point here.
newProject.Events.Add(new ProjectCreatedEvent(newProject));
But this is in UI, away from the domain model, and so does not feel right.
The other option is using ef core interceptors. So when ever save changes is called, just raise event as appropriate something like here.
And if I add event in Project ctor, then this is triggered even in case of an update.
public Project(string name)
{
Name = Guard.Against.NullOrEmpty(name, nameof(name));
var newProjectCreatedEvent = new NewProjectCreatedEvent(this);
Events.Add(newProjectCreatedEvent);
}
Are there any better options/patterns?
Any pointer is deeply appreciated.
When you need to raise a domain event on project creation I would create a factory method that publishes the event.
You can use a static method or implement a factory object.
public class Project : BaseEntity, IAggregateRoot
{
public static Project newProject(string name)
{
var project = new Project(name);
var newProjectCreatedEvent = new NewProjectCreatedEvent(project);
Events.Add(newProjectCreatedEvent);
return project;
}
private Project(string name)
{
Name = Guard.Against.NullOrEmpty(name, nameof(name));
}
}
I want to use interfaces for both client and server in the same android app. Usecase is to run a okhttpmockwebserver serving gRPC requests within the same app the client is running in. For this i created two library projects with their own wire configuration for client and server similar to those
wire {
kotlin {
includes = ['com..caompany.android.proto.*']
out "${buildDir}/protos"
rpcCallStyle = 'suspending'
rpcRole = 'client'
}
}
wire {
kotlin {
includes = ['com..company.android.proto.*']
out "${buildDir}/protos"
rpcCallStyle = 'suspending'
rpcRole = 'server'
}
}
Executing the wire-gradle-plugin fails with this exception:
com.company.android.proto.HelloReply$Companion$ADAPTER$1 is defined multiple times.
Caused by: com.android.tools.r8.CompilationFailedException: Compilation failed to complete, origin: .../com/company/android/proto/HelloReply$Companion$ADAPTER$1.dex
It would help me if wire could either
Generate all classes and interfaces at once including server and client role or
Exclude the generation of class files, only generating service interfaces for client or server
Is there a workaround i can achieve a similar result without gradle plugin support?
You can have multiple kotlin blocks at the same time. Wire will throw if you generate the same class twice so you need to define the rule as unique between both.
You need one block which will generate client role interfaces. You need one block to generate server roles interfaces. Lastly, you need to generate regular types in yet another block, or in one of them (but not both).
Something like this
wire {
kotlin {
includes = ['all.services.or.package']
rpcCallStyle = 'suspending'
rpcRole = 'client'
}
kotlin {
includes = ['all.services.or.package']
rpcCallStyle = 'suspending'
rpcRole = 'server'
}
kotlin {
excludes = ['all.services.or.package']
rpcRole = 'none'
}
}
I'm using Autofac for DI with the builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest(); but sometimes it gives an error on all my controllers (System.InvalidOperationException: An error occurred when trying to create a controller of type 'UserController'. Make sure that the controller has a parameterless public constructor. ---> Autofac.Core.DependencyResolutionException)
and when I give a rebuild of WEB API and it starts working fine.
Here is my code in startup.cs
private void ConfigureAutofac(HttpConfiguration config, IAppBuilder app)
{
var builder = new ContainerBuilder();
//Register HttpRequestMessage
builder.RegisterType<CurrentRequest>().InstancePerRequest();
builder.Register(c => new UrlHelper(c.Resolve<CurrentRequest>().Value));
//Register Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
//Register the Autofac filter provider.
builder.RegisterWebApiFilterProvider(config);
//Register the Autofac model binder provider.
builder.RegisterWebApiModelBinderProvider();
#region Register managers
var businessasm = BuildManager.GetReferencedAssemblies()
.Cast<Assembly>()
.Where(n => n.FullName.Contains("Business"))
.FirstOrDefault();
builder.RegisterAssemblyTypes(businessasm)
.Where(t => t.Name.EndsWith("Manager") && t.Name != "DocumentManager")
.AsImplementedInterfaces()
.InstancePerRequest();
builder.RegisterGeneric(typeof(DocumentManager<>))
.As(typeof(IDocumentManager<>))
.InstancePerRequest();
#endregion
//Set the dependency resolver to be Autofac.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
config.MessageHandlers.Insert(0, new ApiDelegatingHandler());
config.MessageHandlers.Insert(1, new ActivityLogHandler());
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
}
The error indicates that UserController (which you didn't show above) takes a parameter that isn't registered with Autofac.
Since it works sometimes and not others, my guess is that the problem is this line:
var businessasm = BuildManager.GetReferencedAssemblies()
.Cast<Assembly>()
.Where(n => n.FullName.Contains("Business"))
.FirstOrDefault();
FirstOrDefault will return null if no assembly is found; and later on when you register all the things that end with Manager it means nothing will get registered.
My guess is that the UserController needs one of these managers, Autofac doesn't have it registered, and instantiation fails. If you have a breakpoint on the next line, you can see if businessasm is null; or, alternatively, switch to .First() instead of .FirstOrDefault() if you always expect the registrations to work.
Oh, and if you happen to have two assemblies with Business in them, you might want to make sure you're getting the right one. Assembly load order isn't guaranteed to be consistent.
That said I see the exception message you posted seems to indicate there are some nested exceptions in the stack. Don't stop reading at the first exception. Unfortunately with DI and other layers in the stack, you get exceptions that have inner exceptions that may, themselves, have inner exceptions... and the full set of messages may actually have more information that can help you troubleshoot.
Autofac has a really good troubleshooting page and Autofac v6 has some pretty detailed diagnostics that might help figure out where the missing things are if you can't figure it out from the exceptions.
I have a code in Web Api Delegating Handler that extract data from request header.
However, I can't register instance in Autofac container because Autofac container require SingleInstance only.
public class ExtractUserNameMessageHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
var userNameFromFrontEnd = request.GetDependencyScope().GetService(typeof (IUserNameFromFrontEnd));
if (userNameFromFrontEnd == null)
{
var updatedContainerBuilder = new ContainerBuilder();
userNameFromFrontEnd = ExtractUserName(request);
if (userNameFromFrontEnd == null)
{
throw new Exception("We've got a request without UserName header");
}
updatedContainerBuilder.RegisterInstance(userNameFromFrontEnd)
.As<IUserNameFromFrontEnd>()
.InstancePerRequest();
var autofacDependencyResolver = GlobalConfiguration.Configuration.DependencyResolver as AutofacWebApiDependencyResolver;
if (autofacDependencyResolver == null)
{
throw new Exception("We can work with Autofac DI container");
}
updatedContainerBuilder.Update(autofacDependencyResolver.Container as IContainer);
}
When I try to update container I get an exception with message - registration can support singleinstance() sharing only.
What does it mean? I can't understand why we have this limitation. But in any cases my first goal - update container with new dependency.
Does anybody have ideas?
(Note: This question was cross-posted to the Autofac forums as well.)
When you register a specific instance, it's effectively a singleton - it's one instance, the instance you provided.
When you try to assign it InstancePerRequest or, really, any other lifetime scope besides SingleInstance, it doesn't make logical sense because you're not going to get a different instance per request (or whatever). You're going to get the exact same instance you registered, which is a singleton.
The exception message is trying to tell you how to avoid incorrect expectations: that it can't provide you a different instance per request even though you told it to because you didn't tell it how to create a new instance, you instead provided a specific instance.
If you need a different instance of an object per lifetime scope/request/whatever, you need to register a type, a delegate, or something else that tells Autofac how to create that new instance.
What that means is that if you want a different IUserNameFromFrontEnd per request, you need to move that logic out of a DelegatingHandler and into an Autofac registration delegate.
// Make sure to register the HttpRequestMessage in the container
// so you can resolve it...
builder.RegisterHttpRequestMessage(httpConfiguration);
// Then, whilst building your root container...
builder
.Register(ctx =>
{
var request = ctx.Resolve<HttpRequestMessage>();
return ExtractUserName(request);
})
.As<IUserNameFromFrontEnd>()
.InstancePerRequest();
Now it will probably do what you're looking to do - because you told Autofac how to create the instance that belongs in each request. It also means you don't need that DelegatingHandler anymore because Autofac will just do the right thing.
More advanced (and probably not useful here, but for completeness):
If, for whatever reason, you still feel like you need to modify the registration directly in the lifetime scope, instead of updating the container you should add the registration when the request lifetime scope is created.
Again, do not update the root container for per-lifetime-scope or per-request dependencies. It's not going to work how you think.
When a new lifetime scope is created, you can add registrations on the fly.
using(var scope = container.BeginLifetimeScope(
builder => builder.RegisterInstance(myfoo).As<IFoo>()))
{
// This will use the registrations in the container
// and the scope. f == myfoo
var f = scope.Resolve<IFoo>();
}
The AutofacDependencyResolver is the thing that creates the request lifetime scope and hands it off to Web API. You can see the full source here. The key method is BeginScope:
public IDependencyScope BeginScope()
{
var lifetimeScope = _container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
return new AutofacWebApiDependencyScope(lifetimeScope);
}
If you create your own AutofacDependencyResolver you can modify how the scope is created:
public IDependencyScope BeginScope()
{
var lifetimeScope = _container.BeginLifetimeScope(
MatchingScopeLifetimeTags.RequestLifetimeScopeTag,
builder => builder.RegisterInstance(myfoo).As<IFoo>());
return new AutofacWebApiDependencyScope(lifetimeScope);
}
This isn't an explicitly supported extension point in the Autofac Web API integration right now - that's why you'd have to create your own resolver.
However, this seems like overkill to solve the thing it appears you're trying to solve. I strongly recommend just registering the delegate with Autofac rather than trying to update existing containers or scopes. You will have far more luck using the path of least resistance.
I am developing a framework.. I managed to build a 1.0 version but Now I have added a new class to the framework but this class is not visible to everything else it seems.. What are the steps to "recompile" or fix this problem?
This could be down to lots of things, but I was in a similar situation yesterday and the cause was my failure to put all the relevant files in the Compile Sources table (found by selecting your framework in the Targets browser, and navigating to the Build Phases section):
If you're using Swift, and you're trying to access the classes in your framework from some other target/framework, you also need to make sure you've marked the classes you're trying to access as public (they're internal by default).
// Mark class as public so it's available to other frameworks
public class Logger {
// Can only access this from this file
private var log: [LogEntry] = []
// Only classes in this framework can access this
var liveLog: LogEntryPriority? = .Info // Can only get at this
// Can access this from anywhere
public func getReady(logLevel: LogEntryPriority, errorsAreFatal: Bool) {
log = []
liveLog = logLevel
self.errorsAreFatal = errorsAreFatal
}
}