Getting InRequestScope working with Ninject and WebApi - asp.net-web-api

I've got an MVC4/WebApi project that I'm trying to wire up with Ninject 3. I would like to share a particular object across a number of entities within request scope, however I understand that I need to provide some sort of implementation of InRequestScope (https://stackoverflow.com/a/10592203/173225). I've looked at the source on GitHub and it appears to simply return HttpContext.Current. I've tried that:
var messages = new List<string>();
kernel.Bind<IList<string>>()
.ToMethod(x => messages)
.WhenMemberHas<ServiceResultMessagesAttribute>()
.InScope(x => HttpContext.Current);
with no luck. I've also tried to use the latest "unstable" Nuget package for Ninject.Web.WebApi (#9018) as recommended in https://groups.google.com/d/msg/ninject/rC2vhj8yvBU/NAIkNA-QrAAJ, but I get the same error (method get_InRequestScope does not have an implementation).
As for the source on GitHub, at the time of writing the last update to the relevant files was 11 months to more than a year ago, so I don't know if that is current with the unstable Nuget package or not (especially given the state of documentation for Ninject).
Can anyone provide a proper working example of how to inject the same instance of an object across more than one component within request scope?
Thanks.

You will need Ninject.Web.Common reference from nuget or elsewhere and use the InRequestScope method.
var messages = new List<string>();
kernel.Bind<IList<string>>()
.ToMethod(x => messages)
.WhenMemberHas<ServiceResultMessagesAttribute>()
.InRequestScope();

Related

AsyncCrudAppService Breaks Swagger When Providing TCreateInput and TUpdateInput

I recently downloaded a Single Page Web Application (Angular) from https://aspnetboilerplate.com/Templates using 3.x target version.
I just simply added a few entities and then started to follow the steps on this page https://aspnetboilerplate.com/Pages/Documents/Application-Services
Things do work well for me to Get, List, Update, and Delete entities when my app service class is just inheriting AsyncCrudAppService<Entities.PhoneBook, PhoneBookDto, long, GetAllPhoneBooksInput>, however when it is inheriting AsyncCrudAppService<Entities.PhoneBook, PhoneBookDto, long, GetAllPhoneBooksInput, CreatePhoneBookInput, and UpdatePhoneBookInput> the swagger definition will no longer load.
GitHub Repo: https://github.com/woodman231/MyPhoneBooks
(which currently does not work and will not load Swagger page).
I can get the swagger page to load by removing CreatePhoneBookInput and UpdatePhoneBookInput from
https://github.com/woodman231/MyPhoneBooks/blob/main/aspnet-core/src/MyPhoneBooks.Application/SimpleCrudAppServices/ISimplePhoneBookCrudAppService.cs#L9
and
https://github.com/woodman231/MyPhoneBooks/blob/main/aspnet-core/src/MyPhoneBooks.Application/SimpleCrudAppServices/SimplePhoneBookCrudAppService.cs#L14
However, again I am still unable to create entities using this default implementation. Any ideas?
I have cloned your repo and run it and I figured out the error, first as I tell you in comments I verified the text log, and it said the next:
System.InvalidOperationException: Can't use schemaId "$CreatePhoneBookInput" for type "$MyPhoneBooks.SimpleCrudAppServices.Dtos.CreatePhoneBookInput". The same schemaId is already used for type "$MyPhoneBooks.PhoneBooks.Dtos.CreatePhoneBookInput"
What happenig is that you have these two classes UpdatePhoneBookInput, CreatePhoneBookInput repeated in SanokeCrudAppServices\Dtos and PhoneBooks\Dtos
You have the classes in both folders with same exact name, and thats the problem, if you change the name in whatever place the swagger definition will load without errors, I have do it like this and everything works fine!
Change the name in one of the places, and all will be working fine
Personally I don't like to use a different Dto for Create and Update for me is easier to user just one Dto for all.
Ok I figured it out. I had also made a DIY AppService and some of the DTO Class Names associated with the DIY App Service clashed with the DTO Class Names associated with the Automated Service. It was acceptable in .NET since they were in different name spaces but once the swagger definition was configured I assume that there was multiple instances of the same DTO Defition. I checked the AbpLogs table but they didn't give me much details as to the specifics of the internal server error while loading the definition. It sure would have been useful to know that.

500.19 Error using Autofac MultiTenancy in Webforms

I'm trying to modernize an existing multi-tenant WebForms with dependency injection using Autofac.
When I try to browse to a tenant directory, I get a 500.19 error.
HTTP Error 500.19 - Internal Server Error
The requested page cannot be accessed because the related configuration data for the page is invalid.
Config Error Cannot add duplicate collection entry of type 'add' with unique key attribute 'name' set to 'ContainerDisposal'
Config Source:
60: <modules>
61: <add name="ContainerDisposal" type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web" preCondition="managedHandler" />
62: <add name="PropertyInjection" type="Autofac.Integration.Web.Forms.PropertyInjectionModule, Autofac.Integration.Web" preCondition="managedHandler" />
These values are definitely not duplicated. I can browse to a page outside of the tenant directory without getting the error.
Here's how I'm creating the multitenant container.
var context = new TenantDbContext();
var tenantRepository = new TenantRepository(context);
var tenantService = new TenantService(tenantRepository);
var tenants = tenantService.List();
var builder = new ContainerBuilder();
var tenantIdStrategy = new TenantIdentificationStrategy();
builder.RegisterType<TenantDbContext>().InstancePerLifetimeScope();
builder.RegisterType<TenantRepository>().As<ITenantRepository>().SingleInstance();
builder.RegisterInstance(tenantIdStrategy).As<ITenantIdentificationStrategy>();
var multitenantContainer = new MultitenantContainer(tenantIdStrategy, builder.Build());
foreach (var tenant in tenants)
{
var connectionString = tenant.Provider;
multitenantContainer.ConfigureTenant(tenant.Name, b =>
{
b.Register(appContext => new AppDbContext(connectionString))
.InstancePerTenant()
.AsSelf();
b.RegisterType<CourseRepository>()
.As<ICourseRepository>();
});
}
_containerProvider = new ContainerProvider(multitenantContainer);
The example application can be found here: https://github.com/TonyaT3PO/WebForms.AutoFac.MultiTenant
I spent about 15 minutes trying to use your repro to see what's up but:
The instructions in the README to just run those EF commands doesn't work. The web app is where the connection strings are defined, but the data project is where the migrations are. Even if you get past that...
The connection strings refer to three different .mdf files that aren't checked in and don't get automatically created.
For future questions, I'd recommend coming up with a minimal repro. For example, the problem isn't with the database, so the repro doesn't need EF or databases or anything. The problem doesn't have to do with bundles or DTOs or repositories or any of that, so all that can go. There's a great article over here about creating a minimal reproducible example.
A minimal repro helps folks answer you. Like, if I can't get your repro running because it's too complex, I'm usually going to bail out and just not answer. If I can get it running - or, better, if the repro is small enough to fit right here in the question - then I'm far more likely to try helping out.
A minimal example can help you, too. I do this myself - remove moving pieces one by one until stuff starts working as expected, then add stuff back until it breaks again. You'll know which thing was added that started breaking and that's the bit you need to fix or ask about.
In the meantime:
I agree that you don't have ContainerDisposal defined twice. However, web.config has inheritance which is why you can define one at the root of your web app and one in a subfolder with overrides.
This is important to know because something that could be happening is that the server or machine level web.config may already have ContainerDisposal defined for you in which case it's not defined twice in the app-level web.config but once inheritance is applied, it is defined twice.
That inheritance thing isn't something we can debug from the repro - that's going to be something on the machine where you're running the app and seeing the problem.
If I could have run your repro, I could have verified whether or not I also see it; but since I can't, I'll have to leave you with "an exercise for the reader" to check that out. This blog article appears old, but it's still valid for web.config inheritance info. Hopefully it can help.
The 500.19 error was probably a red herring. My applicationHost.config file became corrupted. I got the same error running a known good project.
After Travis's feedback, I created a new, stripped-down project that mimics the minimal things I needed to accomplish.
Each tenant has its own directory.
The tenant pages load a user control.
The user control displays the tenant's data.
Pass the tenant's connection string to a class that needs it to get the data.
The new example can be found here https://github.com/TonyaT3PO/WebForms.Autofac.MultiTenant.Minimal

Dependency injected DbContext in a SignalR hub does not pull latest data

In my ASP.NET Core MVC site, I have a SignalR hub that has my DbContext injected into the constructor. The hub pulls data from the database and send it to a kendo ui grid for the user to view. This data is filtered in that hub on which group is selected (stored in the database).
The group selection is done outside the context of the hub. When I change the users selected group the page reloads to update various UI elements. When the signalr hub is then called, the selected group is still set as what it was prior to the change. After digging for a bit I came across this issue on the signalr github. What I understand is because the hub is transient, the DbContext is as well and since the hub is long running the DbContext is never updated.
Is there a simple way around this while still dependency injecting the DbContext or do I need to create and dispose a new context for every call? If so what is the best way to go about doing that and still pass the connection string from the appsettings.json?
EDIT
I am currently using Microsoft.AspNetCore.SignalR.Server and not the new Microsoft.AspNetCore.SignalR library.
The only way I could get around this issue with Microsoft.AspNetCore.SignalR.Server was to add a DbContextOptionsBuilder<T> singleton to the the ConfigureServices method in Startup.cs and then call that in a using(...) in the hub. While I feel this is a dirty way around the issue, I also believe it is the only way around the issue. Microsoft recently deprecated SignalR-Server and are moving to a new code base at SignalR. Hopefully this issue will be addressed in their new version.
Startup.cs
DbContextOptionsBuilder<PortalDbContext> builder = new DbContextOptionsBuilder<PortalDbContext>();
builder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
services.AddSingleton(builder.Options);
Hub Classes
using (PortalDbContext dbContext = new PortalDbContext(_dbContextOptions))
{
...
}

Returning child elements in ASP.NET WebAPI OData

I'm using the latest ASP.Net WebAPI Nightly builds (dated 2013-01-16).
I have a simple EF database first model at the moment that has two entities - Patients and Visits. Each patient can have many visits.
I'd like to be able to query for my list of patients and have the visits entities for each patient returned inline. I know that WebAPI's OData implementation doesn't yet support $expand. I'm hoping that just means that optional client-controlled expansion is not supported and that I can force expansion server-side.
At the moment I'm not getting any of the visits inline.
For example, my PatientController's() Get() method looks like
[Queryable(AllowedQueryOptions=AllowedQueryOptions.Supported)]
public override IQueryable<Patient> Get()
{
var query = this.entities.Patients.Include("Visits");
return query;
}
I've verified that the query executing against my database does indeed include the visit information.
To use a publicly available OData service as an example, if you use the service at http://services.odata.org/OData/OData.svc/, you can get a list of Suppliers. This is http://http://services.odata.org/OData/OData.svc/Suppliers.
You can also ask for a list of suppliers that includes the list of products using http://http://services.odata.org/OData/OData.svc/Suppliers?$expand=Products
Stepping through the ASP.NET code (via the symbols server) I've got to the System.Web.Http.OData.Formatter.Serialization.ODataEntityTypeSerializer and can see that it's CreatePropertyBag method, which builds up the list of properties to be serialized, just doesn't include the navigation properties, and they don't seem to be enumerated anywhere else apart from being written out as NavigationLinks.
I'm quite new to the ASP.NET world in general and have spent a week or so getting my head around the way things work (particularly with the changes made to OData at the end of 2012 and further changes made so far in 2013).
I suspect that if the ODataEntityTypeSerializer was to be modified (I'm happy to try) to embed this extra information in the appropriate spot (within each navigation link as an nested inline feed as best I can tell) then I'd be set.
Questions:
Have I overlooked something obvious and there's a flag I can set to turn on this behaviour? I can see why, if such a flag exists, it would be off by default (EF lazy loading and this flag wouldn't get on well)
If #1 is no, is there some other ODataEntityTypeSerializer that I could use? If so, how do I switch to it?
If #2 is no, any pointers for where I should start writing my own? Is there a place I can substitute in my own serializer or do I have to maintain my own fork of ASP.NET's Extensions project (as opposed to the Runtime project)
Thanks very much!
$expand is very high on our list of things to support for OData. But as far as I know, we don't have any flag to turn it on server-side. The formatter doesn't currently allow you to substitute your own serializers either. So I'm afraid your only option in the meantime is to create a fork and add support for $expand. If you manage to get it working, please consider sending a pull request our way:
http://aspnetwebstack.codeplex.com/SourceControl/network
You can try it already in webapi nightly builds.
Here is how to install it with nuget:
http://aspnetwebstack.codeplex.com/wikipage?title=Use%20Nightly%20Builds

ASP.NET MVC 3, RavenDB, & Autofac Issue Plus 2 Other Autofac Questions

NOTE: There are 3 questions in here and I did not make separate questions since they are all somewhat related to the same code.
I have the following code that registers the connection to my RavenDB in the Application_Start once per the application's life cycle:
var store = new DocumentStore { Url = "http://localhost:8080" };
store.Initialize();
builder.RegisterInstance(store).SingleInstance();
Now this works fine and this is something that should be created only once per the application's life cycle. Now I wanted to add in the DocumentSession to Autofac so I tried to add in this in the Application_Start:
var session = store.OpenSession();
builder.RegisterInstance(session).SingleInstance();
In my UserRepository I have the following constructor:
public UserRepository(DocumentStore store, DocumentSession session)
When I try to run this, I get the follow runtime error:
Cannot resolve parameter 'Raven.Client.Document.DocumentSession Session' of constructor 'Void .ctor(Raven.Client.Document.DocumentStore, Raven.Client.Document.DocumentSession)'
That error to me sounds like Autofac does not think it has a DocumentSession however that is what store.OpenSession() returns so it should. Anyone know what would be causing this error? Am I not setting the session variable correctly (it is the same as the store variable which works fine)?
Another thing which may or may not be related to the above issue is how do I add an instance of an object to Autofac per request instead of per the applications life cycle? While the RavenDB DocumentStore object should only be created once be the life application cycle, the DocumentSession should be created once per the request (maybe creating it per application level is causing the error above).
One last question I will throw there about Autofac (mildly related to the code above) is about releasing the objects. If you take a look at this tutorial:
http://codeofrob.com/archive/2010/09/29/ravendb-image-gallery-project-iii-the-application-lifecycle.aspx
The last piece of code:
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
and the point of this code is to prevent leaking the sessions. Now is this something I also need to worry about for Autofac and if so, how would I do this in Autofac?
I'm guessing you want something like:
builder.Register(c => c.Resolve<DocumentStore>().OpenSession()).InstancePerLifetimeScope();
"The default ASP.NET and WCF integrations are set up so that InstancePerLifetimeScope() will attach a component to the current web request or service method call." - Autofac: InstanceScope
Basically, in a web app, InstancePerLifetimeScope handles the one per HTTP context aspect, and also disposes any types that implement IDisposable.
There was also the issue that OpenSession returns a IDocumentSession instead of a DocumentSession. Changing my class to look for a IDocumentSession along with doing what Jim suggested worked, thanks.

Resources