Access NancyContext outside Module - nancy

I have a "service" class that does certain logic outside of my Nancy Modules. The service class is registered with the container so that I may access an instances in my modules through contructor injection. If my class depends on the NanyContext, how can I access it from outside a module?

The NancyContext is created per request, so taking a dependency on it only makes sense if the scope of your service is no longer than a request. Otherwise you will have to pass in NancyContext with the method calls you make to your service.
If the service has request scope you could create and register it in the ConfigureRequestContainer of the Bootstrapper:
public class Bootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{
var service = new Service(context);
container.Register(service);
base.ConfigureRequestContainer(container, context);
}
}

Related

How can I initialize a Prism module without View and ViewModel for working with EventAggregator?

I am writing an application using Prism that contains three modules. First one has a view to configure a "Person", second one is a service that generates that "Person" and third one is the visualization of all people. These three modules communicate with EventAggregator system. But I have problems with the messages on the service one.
In this service module I only have the service implementation and the module definition.
This service is a people manager that receives a message from EventAggregator, creates a "Person" with a task and send a message to the third module with this "Person".
Service:
private List<Person> people = new();
public PeopleControllerService(IEventAggregator eventAggregator, ICommonParametersService commonParameters)
{
this._eventAggregator = eventAggregator;
eventAggregator.GetEvent<GeneratePersonEvent>().Subscribe(GeneratePerson);
this._commonParameters = commonParameters;
}
private void GeneratePerson()
{
Person newPerson = new(this._commonParameters.DefaultPersonTask);
this.People.Add(newPerson);
this._eventAggregator.GetEvent<AssignedPersonEvent>().Publish(newPerson);
}
Module definition:
private PeopleControllerService moduleController;
public void OnInitialized(IContainerProvider containerProvider)
{
IEventAggregator eventAggregator = containerProvider.Resolve<IEventAggregator>();
ICommonParametersService commonParametersService = containerProvider.Resolve<ICommonParametersService>();
this.moduleController = new(eventAggregator, commonParametersService);
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
The problem is that when I send the "GeneratePersonEvent" message it never reaches the PeopleControllerService and the "GeneratePerson" method is never executed.
I've tried using a view and a viewModel, programming the service in the viewModel and assigning the view to a dummy and hidden region in the app and I've verified that it works that way.
Modified module definition:
public void OnInitialized(IContainerProvider containerProvider)
{
IRegionManager regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RequestNavigate(RegionNames.DummyRegion, "PeopleController");
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<PeopleController>();
}
How can I use the EventAggregator without using a dummy view? Do I have to add something in the "RegisterTypes" method? I've tried with:
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<PeopleControllerService>();
}
but it doesn't work either.
I've checked this post: Can I get EventAggregator Subscribe Message without view, viewmodel in prism?, and there it says that it is possible, but doesn't describe how to implement.
Most of the time you want exactly one instance of a service, and you have to tell the container:
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<PeopleControllerService>();
}
Also, you want your service to implement an interface so that you can pass different implementations to the consumers of your service, the most obvious case is your tests.
You need to actually create the instance of your service, too. Normally, you inject it into some consumer, but if it's completely decoupled and only talks through the event aggregator, you have to create the instance manually:
// in App.xaml.cs
protected override void OnInitialized()
{
Container.Resolve<PeopleControllerService>();
base.OnInitialized();
}
Hint: if the service implements an interface, the application doesn't need to personally know the controller module.

nancy selfhost, with dryioc and bootstrapper -> Method not found exception

At work we require a small application, and decided to test some things we are not really familiar with for this project as well.
I started with a console app, using topshelf to host nancy via the nancy selfhosting package.
All of this works.
Now I wanted to wire in the DryIoc container into the nancy bootstrapper and got the following error:
System.MissingMethodException
HResult=0x80131513
Message=Method not found: 'DryIoc.Rules DryIoc.Rules.With(DryIoc.FactoryMethodSelector, DryIoc.ParameterSelector, DryIoc.PropertiesAndFieldsSelector, Boolean)'.
Source=Nancy.Bootstrappers.DryIoc
StackTrace:
at Nancy.Bootstrappers.DryIoc.DryIocNancyBootstrapper.<GetApplicationContainer>b__0(Rules rules)
at DryIoc.Container..ctor(Func`2 configure, IScopeContext scopeContext)
at Nancy.Bootstrappers.DryIoc.DryIocNancyBootstrapper.GetApplicationContainer()
at Nancy.Bootstrapper.NancyBootstrapperBase`1.Initialise()
at Nancy.Hosting.Self.NancyHost..ctor(INancyBootstrapper bootstrapper, HostConfiguration configuration, Uri[] baseUris)
at Nancy.Hosting.Self.NancyHost..ctor(Uri baseUri, INancyBootstrapper bootstrapper, HostConfiguration configuration)
at Segrey.Licensing.Web.WebService.Start() in I:\git projects\Segrey.Licensing\Segrey.Licensing.Web\WebService.cs:line 21
at Segrey.Licensing.Service.Program.<>c.<CreateHost>b__1_2(WebService ls) in I:\git projects\Segrey.Licensing\Segrey.Licensing.Service\Program.cs:line 26
at Topshelf.ServiceConfiguratorExtensions.<>c__DisplayClass2_0`1.<WhenStarted>b__0(T service, HostControl control)
at Topshelf.Builders.DelegateServiceBuilder`1.DelegateServiceHandle.Start(HostControl hostControl)
at Topshelf.Hosts.ConsoleRunHost.Run()
My bootstrapper: empty, no registrations yet as I first wanted to test this out
before making it more complex
public class Bootstrapper : DryIocNancyBootstrapper
{
protected override void ApplicationStartup(IContainer container, IPipelines pipelines)
{
//No registrations should be performed in here, however you may
//resolve things that are needed during application startup.
base.ApplicationStartup(container, pipelines);
}
protected override void ConfigureApplicationContainer(IContainer container)
{
//Perform registation that should have an application lifetime
base.ConfigureApplicationContainer(container);
}
protected override void ConfigureRequestContainer(IContainer container, NancyContext context)
{
//Perform registrations that should have a request lifetime
base.ConfigureRequestContainer(container, context);
}
protected override void RequestStartup(IContainer container, IPipelines pipelines, NancyContext context)
{
// No registrations should be performed in here, however you may
// resolve things that are needed during request startup.
base.RequestStartup(container, pipelines, context);
}
}
And in my .Start method for the service via topshelf where the bootstrapper gets assigned
public void Start()
{
var hostConfig = new HostConfiguration() { UrlReservations = new
UrlReservations { CreateAutomatically = true } };
var uri = new Uri(_hostUrl);
_nancyHost = new NancyHost(uri, new Bootstrapper(), hostConfig);
_nancyHost.Start();
}
Error occurs on new NancyHost(uri, new Bootstrapper(), hostConfig); as soon as I add new Bootstrapper() into the arguments
Sounds like a versioning issue. Check your .config file for binding redirects for dryioc.
It appears DryIocNancyBootstrapper thinks it's using version X of DryIOC, but instead it's using version Y (due to a binding redirect), which doesn't have that method (either it was removed or it's an older version that doesn't yet have it).
Figure out what version DryIocNancyBootstrapper wants (i.e., version X in the example above) and reference that version.

How to inject HttpServletRequest into a Spring AOP request (custom scenario)?

I know the standard way of writing an AOP advice around a controller method and that you can get access to the HttpServletRequest arg, if declared in controller method.
But my scenario is that I have a translation service, that is currently session-scoped maintaining the user's locale for translation. I feel this makes the service stateful and also I do not want it to be session-scoped, as I think it is really Singleton. But there are multiple places where the translation service methods are called and so I do not want to change the signature to add request/locale in these methods. The problem is that all the callers of the translation service's methods do not have access to HttpServletRequest (not controller methods)? Can I write an aspect around the translation service methods and somehow magically get access to HttpServletRequest regardless of whether it is available in the caller's context or not?
#Service
public class TranslationService {
public void translate(String key) {
...
}
}
#Aspect
#Component
public class LocaleFinder {
#PointCut("execution(* TranslationService.translate(..))")
private void fetchLocale() {}
#Around("fetchLocale()") // in parameter list
public void advice(JoinPoint joinpoint, HttpServletRequest request) { .... }
}
If now, the caller of translate does not have HttpServletRequest, can't I get request in the advice? Is there a workaround?
Can I write an aspect around the translation service methods and
somehow magically get access to HttpServletRequest regardless of
whether it is available in the caller's context or not?
Not easily. Actually, it would require a lot of effort.
The easy way to do it is to rely on RequestContextHolder. In every request, the DispatcherServlet binds the current HttpServletRequest to a static ThreadLocal object in the RequestContextHolder. You can retrieve it when executing within the same Thread with
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
You can do this in the advice() method and therefore don't need to declare a parameter.
You should be able to auto-wire a HttpServletRequest in your aspect. Spring provides a proxy to the current thread local request instance that way.
So just add:
#Autowired private HttpServletRequest request;
to your aspect. Better yet is to use constructor injection.

Registering Startup Class In Nancy Using AutoFac Bootstrapper

I've been reading through a lot of the Jabbr code to learn Nancy and trying to implement many of the same patterns in my own application. One of the things I can't seem to get working is the concept of an on application start class. The Jabbr code base has an App_Start folder with a Startup.cs file (here) in it with the following implementation.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
...
SetupNancy(kernel, app);
...
}
}
private static void SetupNancy(IKernel kernel, IAppBuilder app)
{
var bootstrapper = new JabbRNinjectNancyBootstrapper(kernel);
app.UseNancy(bootstrapper);
}
When I tried to do something similar to that in my project the Startup.cs file was just ignored. I searched the Jabbr code base to see if it was used anywhere but I wasn't able to find anything and the only differences I could see is Jabbr uses Ninject while I wanted to use AutoFac
Is there a way to register a startup class in nancy?
Take a look at my project over on GitHub, you'll be interested in the Spike branch and may have to unload the ChainLink.Web project to run I can't remember.
I had some trouble finding a way to configure the ILifetimeScope even after reading the accepted answer here by TheCodeJunkie. Here's how you do the actual configuration:
In the bootstrapper class derived from the AutofacNancyBootstrapper, to actually configure the request container, you update the ILifetimeScope's component registry.
protected override void ConfigureRequestContainer(
ILifetimeScope container, NancyContext context)
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDependency>();
builder.Update(container.ComponentRegistry);
}
The application container can be updated similarly in the ConfigureApplicationContainer override.
You should install the Nancy.Bootstrappers.Autofac nuget, inherit from the AutofacNancyBootstrapper type and override the appropriate method (depending on your lifetime scope requirements: application or request). For more info check the readme file https://github.com/nancyfx/nancy.bootstrappers.autofac
HTH
After following the advice from TheCodeJunkie you can use the Update method on the ILifetimeScope container parameter which gives you a ContainerBuilder through an Action:
protected override void ConfigureRequestContainer(ILifetimeScope container, NancyContext context)
{
container.Update(builder =>
{
builder.RegisterType<MyType>();
});
}

Register views with regions depending on the user rights in Silverlight

We have a module class which implements the IModule interface and it has only one method Initialize() in which we register our views.
Is it possible to register these views after successful login?
I want to prohibit the registration of several views depending on the current user. But the user logs on after the module initialization.
Is there a way to provide a callback where Prism can evaluate if the registration is active? Or do I have the chance to disable registrations of the Region Manager? Any other ideas?
Thanks
The easiest way to communicate among multiple modules (or even within) in Prism is using the EventAggregator. Here is what I would do:
Create a CompositePresentationEvent for UserLoginEvent that takes relevant parameter regarding the user
Publish the event using IEventAggregator when the user successfully logs in
On your module initialize, subscribe to the UserLoginEvent, in the handler register the appropriate views
Repeat the above, but opposite, for UserLogout if desired.
In your infrastructure lib:
public class UserLoginEvent : CompositePresentationEvent<User> { }
Then in your module:
public class YourModule : IModule
{
private readonly IUnityContainer container;
private readonly IRegionManager regionManager;
private readonly IEventAggregator events;
public YourModule(IUnityContainer container, IRegionManager manager, IEventAggregator events)
{
this.container = container;
this.regionManager = manager;
this.events = events;
}
public void Initialize()
{
....
events.GetEvent<UserLoginEvent>().Subscribe(RegisterUserViews);
}
private void RegisterUserViews(User u)
{
// check user permissions
// register and create views using container, regionManager
}
}
In whatever module / code your user logs in... I assume you can get the IEventAggregator (similar to above) and then do something like:
OnUserLogin(User u)
{
eventAggregator.GetEvent<UserLoginEvent>().Publish(u);
}
Hope this helps! if a user logs out, then you may want to store references to your views in your module and remove them on a UserLogoutEvent which would work like the above code but just doing the opposite.

Resources