Ninject session scope concept in MVC3 - c#

I am building a MVC3 app using Ninject framework. I have a service that is time-consuming to initialize, and at the end this service will has an object that contains user-specific information, then I need to re-use that service as long as the user session is active, so that I can avoid to initialize that service again and again
So my question is
When I bind the service using Ninject what kind of scope should I pick, there is no session per scope in Ninject, so what is the best way to implement the requirement? or did I went to a wrong direction at all?
I've created a custom provider for one of my services that will create the service based on username details that is grabbed from current Controller.User.Identity.Name. The code below won't work because the userName local variable is missing, how can I pass the user name value into my custom provider via Ninject, so that I can pick it up from IContext??
public class TfsConnectionManagerProvider : Provider<TfsConnectionManager>
{
protected override TfsConnectionManager CreateInstance(IContext context)
{
Uri serverUri = new Uri(ConfigurationHelper.TfsServerUrl);
// Connect to the server without impersonation
using (TfsTeamProjectCollection baseUserConnection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(serverUri))
{
// Get the identity management service
IIdentityManagementService ims = baseUserConnection.GetService<IIdentityManagementService>();
// Get the identity to impersonate
TeamFoundationIdentity identity = ims.ReadIdentity
(
IdentitySearchFactor.AccountName,
userName, //NOTE: How can I get user name value from IContext???
MembershipQuery.None,
ReadIdentityOptions.None
);
// Connect using the impersonated identity
using (TfsTeamProjectCollection impersonatedConnection = new TfsTeamProjectCollection(serverUri, identity.Descriptor))
{
WorkItemStore store = impersonatedConnection.GetService<WorkItemStore>();
return new TfsConnectionManager
{
Store = store
};
}
}
}
}

A session scope is intentionally not offered in Ninject, because having services in a session state is wrong in almost every situation. You should be very carefully about using session state because it brings a lot of disadvantages.
Try to have a stateless application in first place.
If there is a good reason for having data in session scope then put that data (not the services) into the session state and use services that are in singleton, transient or request scope for the processing (separation of data and functionality).

I turn out to use custom Provider for creating the instance and in the custom provider I checked if it exists in session or not.
The binding is done as following
Bind<IRepository>().ToProvider(new TfsRepositoryProvider());
The custom Provider is below
public class TfsRepositoryProvider : Provider<TfsRepository>
{
private const string SesTfsRepository = "SES_TFS_REPOSITORY";
protected override TfsRepository CreateInstance(IContext context)
{
// Retrieve services from kernel
HttpContextBase httpContext = context.Kernel.Get<HttpContextBase>();
if (httpContext == null || httpContext.Session == null)
{
throw new Exception("No bind service found in Kernel for HttpContextBase");
}
return (httpContext.Session[SesTfsRepository] ?? (
httpContext.Session[SesTfsRepository] = new TfsRepository(context.Kernel.Get<IWorkItemStoreWrapper>()))
) as TfsRepository;
}
}

Okay, you can cache / store the user information in your application and only call the external service if you don't have (recent) user information. In your user information retrieval "layer", you just program those two possibilities.
Where you cache, it entirely up to you. You can store this information for example in a local database.
Apparently I understood you wrong, my apologies (below my original answer).
You can use for example an (abstract) factory that holds a static
member of your service (for example) so it will be reused.
Although depending on your service, this might have some unwanted side
effects (I did this once with Data Services and in an ASP.NET MVC3
application my data context was kinda screwed due to some magic that
happened). All I want to say with this is: be careful and test it
well.

Related

Autofac, best practices on runtime adding tenants?

I am using the multitenant container and each tenant has its own database + connectionstring registered in a InstancePerLifeTime scope. The tenant is identified using a subdomain which is mapped in a "master database" with a generated database name.
Now I have two use cases:
Use Case A: Creating new Tenants:
Someone fills in a registration form with the companyname, submits, and after submission we generate a new database and that tenant should be able to access the application under companyname.domain.com
However we want to do that without restarting the application which impacts all current tenants.
Let's say I want to add a new tenant, runtime. What is the best way to register this without restarting the application?
At first I thought about registering the container, inject it in my MVC Controller, and add the new registration runtime but after reading some questions this appears to be bad practice.
I could also get the DependencyResolver from within the Controller and access the container from there. Are there better practices available?
Use Case B: Register on demand
Assuming we have a big amount of tenants and want to prevent registering them all at once on application startup. We could register these in the multitenantcontainer on the first request when the subdomain can be matched to an existing account.
This might be premature optimization though, since basically we don't have lots of tenants yet.
But again, this would result in runtime registrations.
Container:
var tenantIdentificationStrategy= new TenantIdentificationStrategy();
var multitenantContainer = new MultitenantContainer(tenantIdentificationStrategy, builder.Build());
var tenants = new[]
{
"companyA.domain",
"localhost"
};
foreach (var id in tenants)
{
var databaseName = $"tenant-{id}";
multitenantContainer.ConfigureTenant(id, b =>
{
// Init RavenDB
b.Register(context => new RavenDocumentSessionFactory(databaseName))
.InstancePerTenant()
.AsSelf();
// Session per request
b.Register(context => context.Resolve<RavenDocumentSessionFactory>()
.FindOrCreate(context.Resolve<IDocumentStore>()))
.As<IDocumentSession>()
.InstancePerLifetimeScope()
.OnRelease(x =>
{
x.SaveChanges();
x.Dispose();
});
});
}
Your best bet is to hold a static reference to the application container somewhere and register your tenants from there. This is pretty common practice and, since your tenant registration code is going to have to "know" what a MultitenantContainer is anyway, it's not going to change your assembly references or spread the "knowledge" of the container around more than it would otherwise have to be.
Create the multitenant container at app startup.
Register the tenants you already know about.
Store the container in a static property somewhere that is globally accessible.
Reference the static property when you need to register a tenant.

How to inject the current user for a request in to a controller / service?

In my WebApi based application I have a Ninject binding for injecting the current user in to things ...
Bind<User>().ToMethod((context) =>
{
User result = new User { UserName = "Guest" };
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
var service = Kernel.Get<IUserService>();
var user = service.GetAll().FirstOrDefault(u => u.UserName == HttpContext.Current.User.Identity.Name);
return user;
}
return result;
});
Pretty straightforward right?
I then have a webApi controller that has a service class injected to its ctor, which in turn has the current user injected in to it, the stack looks something like this ...
class SomethingController : ApiController {
public SomethingController(ISomethingService service) { ... }
}
class SomethingService : ISomethingService {
public SomethingService(User user) { ... }
}
Frustratingly for some odd reason when i inject the current user in to the service constructor the user is not authenticated yet.
It appears that the stack is being constructed before the authentication provider (aspnet identity) has done its work and confirmed who the user is.
Later when the actual service call is made from the controller the user is authenticated and the user object I have been given is for a guest not the actual user that made the call.
My understanding was that a controller was not constructed until authentication and determining the request / user details was done.
How can I ensure that the current, authenticated user is passed to my business services correctly every time (only after authentication has taken place)?
EDIT:
I think I found the problem: dotnetcurry.com/aspnet/888/aspnet-webapi-message-lifecycle it looks like the controller and all its dependencies are created before any auth logic is run. I guess the question then becomes ... can I force the AuthoriseFilter to run and confirm the users identity before the controller is constructed.
This changes my question somewhat to become:
How do I ensure authentication occurs before the controller and all its dependencies are constructed?
EDIT 2: An Answer - Not ideal but an answer ...
Can someone with more rep unlock this question please, this is not a duplicate of the other question, the OP is asking about the basica usage of Ninject I am asking about WebApi lifecycle and how to get session related context prior to the session being known for the current request.
Ok I don't like this answer but its a solution.
If anyone else has a better one I would love to hear it ...
If I update my service constructor to this ...
class SomethingService : ISomethingService {
public SomethingService(IKernel kernel) { ... }
}
... and I drop the kernel in to a local field, when I come to run my service code ...
public void Foo() {
var user = kernel.Get<User>();
}
... What this means ...
I get the user at the point of the request lifecycle where authentication and authorisation has taken place, and the stack is correctly constructed.
Now if I ask for the user, when the rule runs the HttpContext is correctly showing the right user details.
It works but ...
This is very much DI antipattern type behaviour, I don't like it and would prefer to find a solution that meant I could Authenticate the user there and then if it hadn't already happened yet but it would basically mean replicating code that the WebApi stack already has (that could mean its easier though) and will take place anyway basically resulting in the Authentication process happening twice.
In the absence of such a solution ... this is a "semi" reasonable workaround.
It's been a while but I later found out the answer to this.
The problem was that whilst I had some auth info in the form of say a an auth token provided as a header value, I was trying to construct information about the user and I may not have even needed it during the request.
I wanted to ensure that all my business services sat behind the controller handling the request would be given the current user when in fact they only needed the auth information so that the auth info could be used to determine and make security related decisions (such as getting the user if appropriate).
In the end I took to creating an IoC binding that could examine my owin context and ask that for the auth information, that auth information I would then inject in to my controller / service as needed.
From there I at least had the contextual information I needed to make the key decisions.

Web Api User Tracking

I am in need of help with Web Api.
I am setting up a multi tenant system when each tenant has there own database of data using code first EF and web api (so that I can create multiple app platforms)
I have extended the standard ASP.NET Identity to include a client id and client model which will store all tenants and their users.
I have then created another context which tracks all the data each tenant stores.
Each tenant holds a database name which I need to access based on the authenticated user.
Not getting the user id from each api controller seems easy:
RequestContext.Principal..... etc then I can get the client and subsequently the client database name to pass to the database context however I am trying to implement a standard data repository pattern and really hate repeating myself in code yet the only way I see it working at the moment is to:
Application calls restful api after authorisation
Web Api captures call
Each endpoint gets the user id and passes it to the data store via the interface and subsequently into the data layer retrieving the database name for the context.
What I have a problem with here is each endpoint getting the user id. Is there a way to "store/track" the user id per session? Can this be achieved through scope dependency or something similar?
I hope that makes sense but if not please ask and I will try to clarify further, any help will be greatly appreciated.
Thanks
Carl
ASP WebApi does not have a session context. You may use a cookie or a request token identifier (pass this token back from login and use this token as a parameter for further API calls).
This is something I've developed some time ago. I'm simply creating a new class deriving from ApiController and I'm using this class as a base for all other API class. It is using the ASP.NET cache object which can be accessed via HttpContext. I'm using the current user-id as a reference. If you need something else, you may use another way of caching your data:
public abstract class BaseController: ApiController
{
private readonly object _lock = new object();
/// <summary>
/// The customer this controller is referencing to.
/// </summary>
protected Guid CustomerId
{
get
{
if (!_customerId.HasValue)
{
InitApi();
lock (_lock)
{
if (User.Identity.IsAuthenticated)
{
Guid? customerId = HttpContext.Current.Cache["APIID" + User.Identity.Name] as Guid?;
if (customerId.HasValue)
{
CustomerId = customerId.Value;
}
else
{
UserProfile user = UserManager.FindByName(User.Identity.Name);
if (user != null)
{
CustomerId = user.CustomerId;
HttpContext.Current.Cache["APIID" + User.Identity.Name] = user.CustomerId;
}
}
}
else
{
_customerId = Guid.Empty;
}
}
}
return _customerId.GetValueOrDefault();
}
private set { _customerId = value; }
}
// ... more code
}
Do not blame me on the "lock" stuff. This code was some kind of "get it up and running and forget about it"...
A full example can be found here.
Maybe I am far from truth but Web API is state less so you dont really have a session to track

is there another way of changing Database Instance in Autofac

I have an application that use multiple Database.
i found out i can change that by using the connection builder. like so :
var configNameEf = "ProjectConnection";
var cs = System.Configuration.ConfigurationManager.ConnectionStrings[configNameEf].ConnectionString;
var sqlcnxstringbuilder = new SqlConnectionStringBuilder(cs);
sqlcnxstringbuilder.InitialCatalog = _Database;
but then i need to change the autofac Lifescope of UnitOfWork so that it will now redirect the request to the good Database instance.
what i found out after quite a while is that i can do it like this from a DelegatedHandler :
HttpConfiguration config = GlobalConfiguration.Configuration;
DependencyConfig.Register(config, sqlcnxstringbuilder.ToString());
request.Properties["MS_DependencyScope"] = config.DependencyResolver.GetRequestLifetimeScope();
The question is, is there any other way to do that, that change the MS_DependencyScope parametter of the request. This solution work but i think it is kind of shady.
here is the registry in DependencyConfig:
public static void Register(HttpConfiguration config, String bdContext = null)
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.Register(_ => new ProjectContext(bdContext)).As<ProjectContext>().InstancePerApiRequest();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerApiRequest();
// Register IMappingEngine
builder.Register(_ => Mapper.Engine).As<IMappingEngine>().SingleInstance();
config.DependencyResolver = new AutofacWebApiDependencyResolver(builder.Build());
config.DependencyResolver.BeginScope();
}
The way the question is described and the way the answer to my comment sounds, you have the following situation:
The application uses per-request lifetime units of work. I see this from your registrations.
Only one database is used in the application at a given point in time. That is, each request doesn't have to determine a different database; they all use the same one until the connection string changes. This is seen in the way the database is retrieved from using a fixed application setting.
The connection string in configuration may change, at which point the database used needs to change.
Assuming I have understood the question correctly...
If the app setting is in web.config (as it appears), then changing the string in web.config will actually restart the application. This question talks about that in more detail:
How to prevent an ASP.NET application restarting when the web.config is modified?
If that's the case, you don't have any work to do - just register the database as a singleton and when the web.config changes, the app restarts, re-runs the app startup logic, gets the new database, and magic happens.
If the app setting is not in web.config then you should probably create a project context factory class.
The factory would serve as the encapsulation for the logic of reading configuration and building the connection to the database. It'll also serve as the place to cache the connection for the times when the setting hasn't changed.
The interface would look something like this:
public interface IProjectContextFactory
{
ProjectContext GetContext();
}
A simple implementation (without locking, error handling, logging, and all the good stuff you should put in) might be:
public class ProjectContextFactory : IProjectContextFactory
{
private ProjectContext _currentContext = null;
private string _currentConnectionString = null;
private const string ConnectionKey = "ProjectConnection";
public ProjectContext GetContext()
{
// Seriously, don't forget the locking, etc. in here
// to make this thread-safe! I'm omitting it for simplicity.
var cs = ConfigurationManager.ConnectionStrings[ConnectionKey].ConnectionString;
if(this._currentConnectionString != cs)
{
this._currentConnectionString = cs;
var builder = new SqlConnectionStringBuilder(cs);
builder.InitialCatalog = _Database;
this._currentContext = new ProjectContext(builder.ToString());
}
return this._currentContext;
}
}
OK, now you have a factory that caches the built project context and only changes it if the configuration changes. (If you're not caching the ProjectContext and are, instead, caching the database connection string or something else, the principle still holds - you need a class that manages the caching and checking of the configuration so the change can happen as needed.)
Now that you have a cache/factory, you can use that in your Autofac registrations rather than a raw connection string.
builder.RegisterType<ProjectContextFactory>()
.As<IProjectContextFactory>()
.SingleInstance();
builder.Register(c => c.Resolve<IProjectContextFactory>().GetContext())
.As<ProjectContext>()
.InstancePerRequest();
The ProjectContext will now change on a per request basis when the configured connection string changes.
Aside: I see odd stuff going on with the request lifetime scope. I see in your registration that you're creating your own request lifetime scope. With this method you shouldn't have to do that. If, however, you find that you still need to (or want to), you need to make sure both the originally-created lifetime scope and the one you created are disposed. Lifetime scopes do not get automatically disposed and do hang onto object references so they can handle disposal. There is a high probability that if you're not handling this properly then you have a subtle memory leak. The Autofac Web API integration will take care of creation and disposal of the request lifetime for you, but if you change out the request lifetime, odd things are going to happen.

Access session in class library.

I am developing an application architecture that uses 2 sub projects:
a) asp.net web application (it covers user interface and business logic) and
b) class library. (it covers data access layer)
After system user successfully logs in , the user information is stored in a session object.
The problem I am facing is when I try to access that session object in class library project(data access layer), it always returns null.
I need to access the session object in class library project because, in my case each user has their own username and password for database access(for security reasons);
So, How how do i read and write from/to session object in class library project
Use the System.Web.HttpContext.Current.Session object.
First of all, as Peri correctly noticed - you need to think again if having separate database logins for each user is a good idea - because you loose connection pooling (different users won't be able to reuse existing connections - and creating a new sql connection is quite expensive).
If you really wish to keep separate database users, I would create interface to abstract session from data access:
public interface ILoginDataService
{
LoginData Current { get; }
}
And implementation would pass login data from session. In such way you won't have session dependency to session in your data access logic - so it will be more testable, also you'll separate concerns.
Here is the code I used within a library to get session information.
public static string Entity()
{
string entity = "";
HttpContext httpContext = HttpContext.Current;
if (httpContext.ApplicationInstance.Session.Count > 0)
entity = httpContext.ApplicationInstance.Session["EntityCode"].ToString();
return entity;
}
I am having an ASP.Net application which uses session. I am able to access it in my app_code files using [WebMethod(EnableSession = true)] for the function. I am not sure whether this is your problem. I also faced session value as null when I removed (EnableSession = true) on the method.
using System.Web;
namespace ClassNameSpace
{
public class Class1 : IRequiresSessionState
{
private string sessionValue => HttpContext.Current.Session["sessionKey"].ToString();
}
}

Categories

Resources