I need an instance of a class to be created only once per user session. How do I register such a class with TinyIoC? I'm using NancyFx.
I ended up writing the following code:
public static class ContainerExtensions {
public static TinyIoCContainer.RegisterOptions SessionScoped<TRegisterType>(this TinyIoCContainer container, NancyContext context, Func<TRegisterType> factory) where TRegisterType : class
{
return container.Register<TRegisterType>((ctx, overloads) =>
{
var key = typeof(TRegisterType).FullName;
var instance = context.Request.Session[key] as TRegisterType;
if (instance == null) {
instance = factory();
context.Request.Session[key] = instance;
}
return instance;
});
}
}
I used the Nancy.Session.InProc NuGet.
Related
I can't find IHttpControllerActivator in asp.net core api
public class WindsorHttpControllerActivator:IHttpControllerActivator
{
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var instance = DependencyContainer.Resolve(controllerType);
if (instance == null)
{
throw new HttpException((int)HttpStatusCode.NotFound, string.Format("{0} cannot be resolved.", controllerType.Name));
}
return (IHttpController) instance;
}
}
Let me know if this rewrite works.
There were changes to the API for third-party DI
using Castle.Windsor;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
public class WindsorControllerActivator : IControllerActivator
{
private readonly IWindsorContainer _container;
public WindsorControllerActivator(IWindsorContainer container)
{
_container = container;
}
public object Create(ControllerContext context)
{
var controllerType = context.ActionDescriptor.ControllerTypeInfo.AsType();
var instance = _container.Resolve(controllerType);
if (instance == null)
{
//throw whatever
}
return (ControllerBase)instance;
}
public void Release(ControllerContext context, object controller)
{
_container.Release(controller);
}
}
these articles were helpful to me # least:
https://kristian.hellang.com/third-party-dependency-injection-in-asp-net-core/
https://medium.com/#nevsnirG/manual-controller-activation-and-dependency-injection-in-asp-net-core-web-api-46aba579b0e
EDIT:
in '''Startup.cs''', don't forget the lines
services.AddSingleton<IControllerActivator>(new WindsorControllerActivator (_container));
My CustomBootstrapper looks like below
public class CustomBootstrapper : DefaultNancyBootstrapper
{
protected override NancyInternalConfiguration InternalConfiguration
{
get
{
//This will tell Nancy it won't have to look in the Nhibernate assemblies for implementations of our interfaces.
return NancyInternalConfiguration
.Default
.WithIgnoredAssembly(asm => asm.FullName.StartsWith("NHibernate", StringComparison.InvariantCulture))
.WithIgnoredAssembly(asm => asm.FullName.StartsWith("Fluent", StringComparison.InvariantCulture))
.WithIgnoredAssembly(asm => asm.FullName.StartsWith("Iesi", StringComparison.InvariantCulture));
}
}
protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{
base.ConfigureRequestContainer(container, context);
//container.Register((c, p) => SessionFactory.OpenSession());
container.Register(SessionFactory.OpenSession());
}
}
I have a repository which accepts ISession as a constructor dependency. When I run my application I get an error saying Unable to resolve type: NHibernate.ISession
I can confirm that
My bootstrapper is picked up by Nanacy
The ISession registration code is executed
I have tried commenting out .WithIgnoredAssembly(asm => asm.FullName.StartsWith("NHibernate", StringComparison.InvariantCulture)) from property InternalConfiguration
I get the error with both of the ways of registering component (one is commented in my code)
I have looked at similar other questions on SO and none has helped so far.
I've done a sample project that uses NH with Nancy. In the bootstrapper I set up:
a BeforeRequest hook that creates the session and binds it to the session context
a AfterRequest hook that commits the session
an OnError hook that rolls back
The code is as follows:
public class Bootstrapper : WindsorNancyBootstrapper
{
// other stuff
protected override void ApplicationStartup(IWindsorContainer container, IPipelines pipelines)
{
base.ApplicationStartup(container, pipelines);
// other setup
ConfigureNHibernateSessionPerRequest(container, pipelines);
}
private void ConfigureNHibernateSessionPerRequest(IWindsorContainer container, IPipelines pipelines)
{
pipelines.BeforeRequest += ctx => CreateSession(container);
pipelines.AfterRequest += ctx => CommitSession(container);
pipelines.OnError += (ctx, ex) => RollbackSession(container);
}
private Response RollbackSession(IWindsorContainer container)
{
var sessionFactory = container.Resolve<ISessionFactory>();
if (CurrentSessionContext.HasBind(sessionFactory))
{
var requestSession = sessionFactory.GetCurrentSession();
requestSession.Transaction.Rollback();
CurrentSessionContext.Unbind(sessionFactory);
requestSession.Dispose();
}
return null;
}
private Response CreateSession(IWindsorContainer container)
{
var sessionFactory = container.Resolve<ISessionFactory>();
var requestSession = sessionFactory.OpenSession();
CurrentSessionContext.Bind(requestSession);
requestSession.BeginTransaction();
return null;
}
private AfterPipeline CommitSession(IWindsorContainer container)
{
var sessionFactory = container.Resolve<ISessionFactory>();
if (CurrentSessionContext.HasBind(sessionFactory))
{
var requestSession = sessionFactory.GetCurrentSession();
requestSession.Transaction.Commit();
CurrentSessionContext.Unbind(sessionFactory);
requestSession.Dispose();
}
return null;
}
}
Is it possible to change the default object scope in Ninject 2.2? If so, how is it done?
As far as I can tell you could override AddBinding() on the BindingRoot (StandardKernel or NinjectModule) and modify the ScopeCallback property on the binding object.
public class CustomScopeKernel : StandardKernel
{
public CustomScopeKernel(params INinjectModule[] modules)
: base(modules)
{
}
public CustomScopeKernel(
INinjectSettings settings, params INinjectModule[] modules)
: base(settings, modules)
{
}
public override void AddBinding(IBinding binding)
{
// Set whatever scope you would like to have as the default.
binding.ScopeCallback = StandardScopeCallbacks.Singleton;
base.AddBinding(binding);
}
}
This test should now pass (using xUnit.net)
public class DefaultScopedService { }
[Fact]
public void Should_be_able_to_change_default_scope_by_overriding_add_binding()
{
var kernel = new CustomScopeKernel();
kernel.Bind<DefaultScopedService>().ToSelf();
var binding = kernel.GetBindings(typeof(DefaultScopedService)).First();
binding.ScopeCallback.ShouldBe(StandardScopeCallbacks.Singleton);
}
The CustomScopeKernel will also work with Ninject modules.
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<DefaultScopedService>().ToSelf();
}
}
[Fact]
public void Should_be_able_to_change_default_scope_for_modules()
{
var module = new ServiceModule();
var kernel = new CustomScopeKernel(module);
var binding = kernel.GetBindings(typeof(DefaultScopedService)).First();
binding.ScopeCallback.ShouldBe(StandardScopeCallbacks.Singleton);
}
Let's say I am using the new WCF Web API to build a RESTful service and, in my service, I have a section of the URI that will describe the target resource, but is used on (nearly) all methods of the contract. For example, if I have a User service that deals with eCommerce and may look like:
[ServiceContract]
public class MyUserService
{
private MyUserRepository _UserRepo;
private MyOrganizationRepository _OrgRepo;
[WebGet (UriTemplate = "{OrganizationName}/Users")]
public IEnumerable<User> GetUsers (string OrganizationName)
{
IEnumerable<User> Users = null;
var Organization = _OrgRepo.GetOrgByName (OrganizationName);
if (Organization != null)
{
Users = Organization.GetUsers ();
}
else
{
throw new WebFaultException<string> ("Organization not found.", HttpStatusCode.NotFound);
}
return Users;
}
[WebInvoke (UriTemplate = "{OrganizationName}/Users", /*yada...yada...yada*/)]
public User AddNewUser (string OrganizationName, User User)
{
// Find the organization, like above, and throw if null.
}
}
If I have to continually load the organization and test for null, this will bog down my code and is not very DRY. (So tempted to spell out DRY...) What I would like to do is load up a property in the MyUserService class that is populated when {OrganizationName} is included in the URI and throw a WebFaultException otherwise. Because this is apart of the URI, what would be the best way to accomplish this?
EDIT:
For those that may be interested, here is an example of the HttpOperationHandler I came up with. There doesn't seem to be a whole lot of information out there covering this. I also found more information about Processors that will be coming with the WCF Web Api suite and it looks like they will handle this sort of thing better replace HttpOperationHandlers and it seems they may be easier to use. (This is just a for-instance to cover some things I found hard to find. I wrote it up a bit differently in my application.)
using Microsoft.ApplicationServer.Http.Dispatcher; // For HttpOperationHandler
using Microsoft.ApplicationServer.Http.Description; // For HttpOperationHandlerFactory
public class OrganizationHandler : HttpOperationHandler<string, Organization>
{
private Repository<Organization> _OrganizationRepository;
public OrganizationHandler (UnitOfWork Work)
: base ("OrganizationName")
{
_OrganizationRepository = Work.Organizations;
}
public override Organization OnHandle (string OrganizationName)
{
var Result = _OrganizationRepository
.Get (O => O.UrlSafeName.Equals (OrganizationName,
StringComparison.InvariantCultureIgnoreCase));
if (Result == null)
{
throw new WebFaultException<string> ("Organization not found.");
}
return Result;
}
}
public class OrganizationHandlerFactory : HttpOperationHandlerFactory
{
private UnitOfWork _Work;
public OrganizationHandlerFactory (UnitOfWork Work)
{
_Work = Work;
}
protected override Collection<HttpOperationHandler> OnCreateRequestHandlers
(ServiceEndpoint endpoint, HttpOperationDescription operation)
{
var Collection = base.OnCreateRequestHandlers (endpoint, operation);
if (operation.InputParameters.Any (IP => IP.Type.Equals (typeof (Organization))))
{
var Binding = endpoint.Binding as HttpBinding;
if (Binding != null)
{
Collection.Add (new OrganizationHandler (_Work));
}
}
return Collection;
}
}
And then to wire it up in Global.asax (I am using Ninject for IoC):
// Add this reference to get the MapServiceRoute<T> extension
using Microsoft.ApplicationServer.Http.Activation;
public class Global : HttpApplication
{
protected void Application_Start (object sender, EventArgs e)
{
var Kernel = BuildKernel ();
var Config = HttpHostConfiguration.Create ()
.SetOperationHandlerFactory
(Kernel.Get (typeof (OrganizationHandlerFactory)) as OrganizationHandlerFactory)
.SetResourceFactory (new NinjectResourceFactory (Kernel));
RouteTable.Routes.MapServiceRoute<OrganizationService> ("Organizations", Config);
}
protected IKernel BuildKernel ()
{
IKernel Kernel = new Ninject.StandardKernel ();
// Load up the Kernel
return Kernel;
}
}
public class NinjectResourceFactory : IResourceFactory
{
private readonly IKernel _Kernel;
public NinjectResourceFactory (IKernel Kernel)
{
_Kernel = Kernel;
}
public object GetInstance (Type serviceType, InstanceContext instanceContext, HttpRequestMessage request)
{
return Resolve (serviceType);
}
public void ReleaseInstance (InstanceContext instanceContext, object service)
{
throw new NotImplementedException ();
}
private object Resolve (Type type)
{
return _Kernel.Get (type);
}
}
And here it is in my Service:
[ServiceContract]
[ServiceBehavior (InstanceContextMode = InstanceContextMode.PerCall)]
public class OrganizationService
{
[WebGet (UriTemplate = "{OrganizationName}/Products")]
public IEnumerable<Product> GetProducts (Organization Organization)
{
return Organization.Products;
}
}
This is exactly what OperationHandlers are for. You create a single OperationHandler that converts the URI parameter into a strongly typed object that you can just accept as a parameter on the operation.
Autofac automatically generates factories for Func<T>; I can even pass parameters.
public class MyClass
{
public MyClass(Func<A> a, Func<int, B> b)
{
var _a = a();
var _b = b(1);
}
}
Can I do the same with Ninject? If not, what workaround can I apply?
Thanks.
Update:
Just found this post, seems the answer is no:
How do I handle classes with static methods with Ninject?
NB Ninject 3.0 and later has this fully supported using the Ninject.Extensions.Factory package, see the wiki:- https://github.com/ninject/ninject.extensions.factory/wiki
EDIT: NB there is a Bind<T>().ToFactory() implementation in Ninject 2.3 (which is not a fully tests supported release but is available from the CodeBetter server)
Ninject does not support this natively at the moment. We planned to add this to the next version. But support can be added easily by configuring the appropriate binding. Just load the module below and enjoy.
public class FuncModule : NinjectModule
{
public override void Load()
{
this.Kernel.Bind(typeof(Func<>)).ToMethod(CreateFunc).When(VerifyFactoryFunction);
}
private static bool VerifyFactoryFunction(IRequest request)
{
var genericArguments = request.Service.GetGenericArguments();
if (genericArguments.Count() != 1)
{
return false;
}
var instanceType = genericArguments.Single();
return request.ParentContext.Kernel.CanResolve(new Request(genericArguments[0], null, new IParameter[0], null, false, true)) ||
TypeIsSelfBindable(instanceType);
}
private static object CreateFunc(IContext ctx)
{
var functionFactoryType = typeof(FunctionFactory<>).MakeGenericType(ctx.GenericArguments);
var ctor = functionFactoryType.GetConstructors().Single();
var functionFactory = ctor.Invoke(new object[] { ctx.Kernel });
return functionFactoryType.GetMethod("Create").Invoke(functionFactory, new object[0]);
}
private static bool TypeIsSelfBindable(Type service)
{
return !service.IsInterface
&& !service.IsAbstract
&& !service.IsValueType
&& service != typeof(string)
&& !service.ContainsGenericParameters;
}
public class FunctionFactory<T>
{
private readonly IKernel kernel;
public FunctionFactory(IKernel kernel)
{
this.kernel = kernel;
}
public Func<T> Create()
{
return () => this.kernel.Get<T>();
}
}
}