Resolving dependency on data model using Autofac - wcf

I have run into an issue while creating a data service and using Autofac WCF Integration to resolve a dependency on my data model. Registrations are of the form:
builder.RegisterType<MyService>()
.InstancePerDependency();
builder.RegisterType<MyModel>()
.InstancePerLifetimeScope();
where MyModel has a dependency on MyProvider
public MyModel (MyProvider provider)
{
_provider = provider;
}
The problem arises as this provider is registered in Request scope for reasons pertinent to my application.
builder.RegisterType<MyProvider>()
.As<MyProvider>()
.InstancePerMatchingLifetimeScope(RequestContextTag);
As might be obvious, request container is created and disposed on each ASP.Net request.
However, MyModel and MyService are registered in Application scope. I came up with two possible solutions -
Change scope of provider (not possible as will have to remodel almost the entire app)
Register service and model in request scope (Don't know if this is possible and if at all, correct)
Any advice/ suggestions appreciated. Thanks.

I guess by your statement "registered in Request scope" that you mean the ASP.NET "request" scope as configured via ContainerProvider? (If not please clarify.)
The request lifetime is only seen by ASP.NET requests, and not WCF requests - WCF uses a different pipeline.
There is probably a way to achieve what you want so please post some details of the MyProvider registration.
Make sure your per-request component doesn't depend on any of the ASP.NET "Http" types, which will not be available during a WCF request regardless of whether Autofac is used or not.
Hope this helps!
Nick

Related

How to add Application Insights to ILogger<T> for Unity DI into WCF Service

I have the unfortunate task of updating a WCF project to .NET Framework 4.8 and replacing the existing log4net exception handling with Microsoft Logging and Application Insights.
My approach was to implement Dependency Injection to make instantiation easier and because I am already used to that design approach with .NET Core. As you may know, WCF services adds its own complexity since WCF is responsible for instantiating the services and lower level objects (like custom customUserNamePasswordValidatorType). I was able to handle most of this by implementing a WCF Service Factory and updating the svc markup with it.
I have added Application Insights to the project via the appropriate NuGet packages which updates the web.config with appropriate httpModules for general exception handling. I was even able to add a custom TelemetryInitializer in Application_Start to include a few custom properties in the message.
My problem is getting an instance of ILogger<T> with the attached Application Insights provider into the instantiated service.
At first, I was getting an exception because of the parameterless constructor of Logger<T>. This was fixed by updating my WcfServiceFactory to the following
container
...
.RegisterType<ILoggerFactory, LoggerFactory>(TypeLifetime.Singleton)
.RegisterType(typeof(ILogger<>), typeof(Logger<>), TypeLifetime.Singleton);
This allowed an instance of the logger to be passed into the constructor of each service. However, calls to log messages are never written to App Insights and an inspection of the logger shows zero providers.
My guess is that because a new instance of LoggerFactory is being created, the already existing instance used in the httpModules is ignored.
According to https://github.com/unitycontainer/microsoft-logging, I need to refactor what I am doing and actually create an instance of a LoggerFactory and manually add the desired providers. My issue is that I cannot figure out how to do that with Application Insights. Everywhere else I used it, I would just call the AddApplicationInsights() extension method but that does not seem to be an option here (at least not with Unity).
So I guess my issue boils down to the following questions
Can I get the WcfServiceFactory to use the existing App Insights logging? Maybe establish something in Global.asx?
How can I manually register App Insights as a provider in the WcfServiceFactory including the custom telemetry?
No need to use ILogger if the intention is just to log a custom error. You can log exception on AppInsights directly using the telemetry client, like this:
var telemetryClient = new TelemetryClient(TelemetryConfiguration.Active);
telemetryClient.TrackException(new Exception("Logged random exception message."));

System.InvalidOperationException: An attempt was made to use the context while it is being configured

I have implemented custom ASP.NET Core middleware which uses database context as a dependency. Sometimes it throws the following exception for the very first request coming to the API:
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: An attempt was made to use the
context while it is being configured. A DbContext instance cannot be
used inside OnConfiguring since it is still being configured at this
point. This can happen if a second operation is started on this
context before a previous operation completed. Any instance members
are not guaranteed to be thread safe.`
I could reproduce it only when API was called from SPA. When I called API from Swagger everything was working just fine. Changing order of middlewares didn't help. After digging around I realized that middleware was instantiated once per application while the database context had a scoped lifetime. So perhaps the issue was in injecting my database context right into middleware's constructor. I fixed my code by removing injection of database context from constructor and injecting it directly to InvokeAsync method. This helped and exception gone.
Though I solved my issue I don't quite understand yet how it worked at all. As far as I understand the EF.Core database context by default is registered with scoped lifetime what in terms of ASP.NET Core application means that new context gets instantiated for every new request and disposed upon its completion. Because I injected the database context into middleware's constructor it should have been disposed right after first request finished and this exceptions or another one saying that it's trying to use already-disposed context should have been thrown. Also it's absolutely not clear to me why this error was happening only when API got called from SPA while it was working good for all Swagger requests.
Seems I have figured it out. I didn't mention that I use Autofac service provider instead of default service provider in my application which in fact was worth to mention. According to ASP.NET Core documentation
The Default Service Provider in Development environment performs
checks to verify that:
Scoped services aren't directly or indirectly resolved from the root service provider.
Scoped services aren't directly or indirectly injected into singletons.
Apparently Autofac service provider doesn't perform such checks so it allows to inject scoped services from root service provider. And according to documentation
The root service provider's lifetime corresponds to the app/server's
lifetime when the provider starts with the app and is disposed when
the app shuts down.
This is why my application worked as expected though I injected scoped service into singleton. It used the instance of DbContext resolved from the root service provider and sometime I could get such situation when I tried to perform query to database before the context was actually initialized. This explains the sporadic nature of this issue.
I had the same issue, I resolved it as mentioned in the answers to this question.
added "ServiceLifetime.Transient" option to the db context.
at Startup.cs added:
services.AddDbContext<TableContext>(opt =>
..otherOptions.., ServiceLifetime.Transient);

Consuming WebApi from Mvc Controllers - HttpClient or reference API assembly?

I have a solution with an MVC application and a Web API. They're in separate projects and domains (using CORS). I built it as a Web API to have the flexibility for adding consumers but currently my MVC application is the only consumer. As such I'm debating whether to use HttpClient inside my MVC Controller or directly instantiate the ApiController.
In either case, I'm using dependency injection (Autofac) so I'd also like to know how to configure that because neither HttpClient or ApiController have any kind of interface that I can inject through constructor parameters like I usually do so I'm not sure how to handle this.
What should the lifetime scopes be for the injected instance? HttpClient should probably be Singleton since it's not encouraged to dispose it after each request.
NOTE By calls to the API return large datasets used to populate charts which is why I'm leaning a bit away from HttpClient as I feel I will incur additional overhead using Http. Is it an antipattern to directly instantiate the ApiController?
Thanks
You could always do this
var httpClient = new HttpClient(new HttpServer(GlobalConfiguration.Configuration));
This allows you to continue using the HttpClient but the requests get passed directly to the WebApi server without ever making a network request. This way if you later decide to separate out your WebAPI your client code doesn't change

Ninject Di bindings using a WCF service

I recently created a WCF service library. I am planning on hosting it in IIS. Since I want to reuse my repository layer I decided to go use Ninject in my WCF service as well (I use it in other projects in the solution).
I installed the Ninject Wcf Extensions. I configured it using the NinjectServiceHostFactory in the svc file. I added a Global.asax file to override the CreateKernel() that inherits from NinjectWcfApplication but I am not sure if I am using the bindings correctly. I first started with:
Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
But I quickly realized that this does not work since no data was saved to my database. It appears that the WCF service does not use the ASP.NET pipeline. I went ahead and tried both of these as well just to see if my data was committed to the database:
Bind<IUnitOfWork>().To<UnitOfWork>().InThreadScope();
Bind<IUnitOfWork>().To<UnitOfWork>().InTransientScope();
No luck. I then decided to try:
Bind<IUnitOfWork>().To<UnitOfWork>().InSingletonScope();
This worked but I don't want my database context to be shared by every single request that comes in to the WCF service. I then did some research and found the following approach:
Bind<IUnitOfWork>().To<UnitOfWork>().InScope(c => OperationContext.Current);
This works but is it correct? I wan t something to resemble the InRequestScope for a MVC application. Each request to the service should get its own Database context.
I suggest to have a look at the latest build from the CI-Server http://teamcity.codebetter.com
You need Ninject, Ninject.Web.Common, Ninject.Extensions.Wcf
With this version you can use InRequestScope for Wcf.
I am new to Ninject, but I can tell you that OperationContext.Current is the equivalent to HttpContext.Current for web application.
So your first thought was to use .InRequestScope(); (which is equivalent to .InScope(c => HttpContext.Current);)
so I guess that using .InScope(c => OperationContext.Current); for WCF is pretty correct.

WCF - StructureMap - Caching objects for the duration of the request only

So I already have a working implementation of StructureMap with the WCF service (including custom instance provider, behaviors, etc.)
When I try to have an object that is instantiated only once per user request, I use the InstanceScope.HttpContext and it throws because the context is null.
Do anyone have a proper way of doing that?
On the server-side of the WCF service? By default, WCF has nothing to do with ASP.NET and thus all your HttpContext etc. aren't there.
By default, your WCF services will be called on a "per-call" basis, e.g. each request gets a brand-new, separate, totally isolated instance of your service class. Why not just put those things into the service class as internal fields??
Or you might want to check out this blog post on how to abstract request state and providing sample implementations for ASP.NET (using HttpContext.Items) and WCF.