8

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
                    };
                }
            }
        }
    }
tereško
  • 58,060
  • 25
  • 98
  • 150
Ming
  • 730
  • 2
  • 8
  • 23

3 Answers3

7

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).

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Remo Gloor
  • 32,665
  • 4
  • 68
  • 98
  • Thanks Remo, it might be confusing that I put session in my original description. I don't mean that I was going to use session to hold the data, the goal is to re-use the user-specific object that returned from the long-running service as much as possible with Ninject framework. Any appropriate design idea will be welcomed – Ming May 04 '12 at 12:54
2

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;
        }
    }
Ming
  • 730
  • 2
  • 8
  • 23
0

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.

Styxxy
  • 7,462
  • 3
  • 40
  • 45
  • Apologize, I might not explain my question well, initialization of the service involves calls to some web services which will then return user specific information. I can't make it static because it's user-specific – Ming May 04 '12 at 08:56
  • Ow, then I misunderstood you :). What you can do is try to cache the user data (information) in your application, and only call the external services if you don't have the user information yourself. – Styxxy May 04 '12 at 09:00
  • Yes, so that comes to my true question above - how can I achieve it via Ninject binding using scope – Ming May 04 '12 at 09:02
  • Thanks for your feedback, I can understand the solution you suggested above which is one of very known designs but what I am really looking for is how it can be achieved under Ninject framework thanks – Ming May 04 '12 at 09:08
  • You don't have to do anything with Ninject here. Just have your class that retrieves the user information chooses whether it gets the information out of the database or from the service. Nothing changes at your ASP.NET MVC 3 side. – Styxxy May 04 '12 at 09:38