ngRx 4 router-store router serializer - ngrx

When using the router-store from ngRx 4 and the router serializer, should we continue to use the built-in router's ActivatedRouterSnapshot for situations like the following (pulling a token off a router parameter inside a component):
constructor(
private appMessengerService: AppMessengerService,
private authenticationStore: Store<authenticationStorePlan.State>,
private route: ActivatedRoute
) {
this.appMessengerService.subscribeToMessenger()
}
ngOnInit() {
const token = this.route.snapshot.paramMap.get('token')
this.validateToken(token)
}
The router serializer shows an example of the root level query params, but in this situation, my component is a child of the root and it seems like a hairy situation to try picking off the exact branches of children for these params instead of just taking them from the component's activated route injection.
I'm trying to make the store the single-source of truth, but it feels hacky be setting the router state slice with the router serializer.
Is there a good way to set up the router serializer to do something similar to the Angular router for params without making the router state object too big for the dev tools ?

Related

Symfony Cache Component - Redis Adapter

I have implemented the Symfony Cache Component using RedisAdapter. Now we like to use a colon as separator in cache keys (e.g. some:cache:key:25). Just like Redis recommends.
I get an Exception saying "contains reserved characters {}()/\#: etc.". This is explained in Symfony Documentation
(https://symfony.com/doc/3.4/components/cache/cache_items.html) that those are reserved characters in PSR-6.
I like to know if there is a way around that? Because i am busy refactoring cache logic using Symfony Cache Component. But keys are already defined, so i am not able to change them without breaking conventions. 😭
As you noted : is a reserved character in the PSR-6 cache standard, which Symfony's cache component builds on.
If you want to keep them in your code, you could write an adapter that takes your keys and replaces the : with something else before passing it to the regular cache.
So for example you could write an adapter that looks something like this:
class MyCacheAdapter implements AdapterInterface
{
private $decoratedAdapter;
public function __construct(AdapterInterface $adapter)
{
$this->decoratedAdapter = $adapter;
}
public function getItem($key): CacheItemInterface
{
$key = str_replace(':', '.', $key);
return $this->decoratedAdapter->getItem($key);
}
...
}
For all other methods you can just proxy the call to the decorated service and return the result. It's a bit annoying to write, but the interface demands it.
In your service configuration you can configure it like this:
services:
App\Cache\MyCacheAdapter:
decorates: 'Symfony\Component\Cache\Adapter\RedisAdapter'
arguments:
$adapter: '#app.cache.adapter.redis'
This configuration is only a rough outline both argument and the class names might have to be adjusted. In any case with this service decoration your adapter wraps around the original redis adapter and then when you configure it to be used by the cache component it should work fine, that your existing keys like some:cache:key25 will be converted to some.cache.key25 before they are passed into the cache component, so before the error message happens.

Understanding the purpose of the ngrx router-store project as compared to only using the angular 2 router

I am in reference to the router-store ngrx project (https://github.com/ngrx/router-store).
I am not clear how to use this project...
For instance let's take the following sample from the project documentation:
store.dispatch(go(['/path', { routeParam: 1 }], { query: 'string' }));
Is this meant to be use as a replacement to the angular 2 router: router.navigate(['/path...?
...or should I use the ngrx router-store only in certain circumstances? (if so which ones?)
Also what happens to the ngrx router-store when a angular 2 router html link e.g. <a routerLink="/heroes" is clicked?
More generally, can someone please explain what is achieved by the ngrx router-store project as compared to using the plain angular 2 router?
Or to rephrase, what does ngrx router-store brings in addition to the angular 2 router?
Edit: An interesting source of information and samples about ngrx is of course the ngrx example-app (https://github.com/ngrx/example-app).
I found a dependency to the router-store there but I have not been able to find where the router-store is used within the app...
FYI, here is the comment to be found in the example app about the router store:
#ngrx/router-store keeps router state up-to-date in the store and
uses the store as the single source of truth for the router's state.
The #ngrx/router-store exists so that it's possible for the store to be the single source of truth for an application's routing state.
Without it, there would be application state - the current route - not represented in the store. That means time-travel debugging using the DevTools would not be possible, as there would be no state in the store representing the route and there would be no actions representing route changes.
The router-store does not replace the Angular router; it just wires up listeners for routing actions and for the router itself.
When you emit a routing action using the go action creator, a "[Router] Go" action containing the specifed path is heard by the router-store which then calls the corresponding router method. When the router-store hears - from the router - that the route has changed it emits a "[Router] Update Location" action representing the route change and that action sees the router state in the store updated.
If, instead of using the go action creator, a routerLink is used to effect a route change, router-store will hear the change and will emit a "[Router] Update Location" action that will see the store's router state updated.
So, whether the route is changed via actions or more traditional links, the store always contains the router state.
With the "[Router] Update Location" actions representing route changes, you can undo said route changes via the DevTools - something that would not be possible if the router state were not represented in the store.
If you've not used the Redux DevTools, I would recommend you check them out:
Redux DevTools Extension
#ngrx/store-devtools
#ngrx/store-log-monitor
An example.
Say you have a selected id that you pass in the router state. That id references a customer.
Your url looks something like this: myapp.com/customers/7755664
When you route to the customer edit view, you can write a selector that gets the customer entity using the id from the router state. Say you want to scroll through the customers. You navigate to myapp.com/customers/7755653. The selector returns the customer, the select call emits and your view rerenders with the new customer.
It simplifies selectors and replaces the need to have a selectedcustomer property in your state.

Simple Injector LifetimeScope with DbContext

I am using simpleInjector 2.8.0.0 I would like to construct just one instance of a dbContext during a lifetime scope.
(My dependency chain has 2 dependencies on IDatabaseContext/DbContext)
I have a scope decorator which implements the lifetime scope (The scope decorator is being injected into a SignalRHub):
using (ServiceHost.Container.BeginLifetimeScope())
{
var commandHandler = ServiceHost.Container.GetInstance<ICommandHandler<TCommand>>();
//constructs 2 DbContexts.
commandHandler.Handle(command);
}
I have a lifetime scope registration for the IDatabaseContext:
container.RegisterLifetimeScope<IDatabaseContext, DatabaseContext>();
However, I can see (from a Guid) that I am creating 2 instances of the IDatabaseContext/DbContext within the scope.
Are you sure this is the same scope? Either you are nesting scopes, or you what you see is two actual request being handled. In the debugger, you might be able to view request information, for instance by inspecting the HttpContext.Current.Request.Url property.

BreezeJS modified route not working

My application has two databases with exactly the same schema. Basically, I need to change the DbContext based on what data I'm accessing. Two countries are in one Db and 4 countries in the other. I want the client to decide which context is being used. I tried changing my BreezeWebApiConfig file so that the route looks like this:
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{dbName}/{controller}/{action}/{id}",
defaults: new {id=RouteParameter.Optional,dbName="db1"}
);
I added the string to the controller actions:
[HttpGet]
public string Metadata(string dbName="")
{
return _contextProvider.Metadata();
}
And changed the entityManager service Name.
Now when the client spins up, it accesses the corrent metadata action and I get a message:
Error: Metadata query failed for: /breeze/clienthistory/kenya/Metadata. Unable to either parse or import metadata: Type .... already exists in this MetadataStore
When I go to the metadata url from the browser, I get the correct metadata (exactly the same as when I remove the {dbName} segment from the route). If I remove the {dbName} segment from the route I get no error and everything works fine
(I have not started implementing the multiple contexts yet -- I am just trying to make the additional segment work).
Thanks.
I think the problem is that your Breeze client is issuing two separate requests for the same metadata, once under each of the two "serviceNames". Breeze tries to blend them both into the same EntityManager.metadataStore ... and can't because that would mean duplication of EntityType names.
One approach that should work is to begin your application by fetching the metadata immediately upon app start and then adding all the associated "DataServiceNames" to the MetadataStore.
Try something along these lines (pseudo-code):
var manager;
var store = new breeze.MetadataStore();
return store.fetchMetadata(serviceName1)
.then(gotMetadata)
.catch(handleFail);
function gotMetadata() {
// register the existing metadata with each of the other service names
store.addDataService(new breeze.DataService(serviceName2));
... more services as needed ...
manager = new breeze.EntityManager({
dataService: store.getDataService(serviceName1), // service to start
metadataStore: store
});
return true; // return something
}
Alternative
Other approaches to consider don't involve 'db' placeholder in the base URL nor any toying with the Web API routes. Let's assume you stay vanilla in that respect with your basic service name
var serviceName = '/breeze/clienthistory/';
..
For example, you could add an optional parameter to your routes (let's call it db) as needed via a withParameters clause.
Here is a query:
return new breeze.EntityQuery.from('Clients')
.where(...)
.withParameters({db: database1}); // database1 == 'kenya'
.using(manager).execute()
.then(success).catch(failed);
which produces a URL like:
/breeze/clienthistory/Clients/?$filter=...&db=kenya
It makes an implicit first-time-only metadata request that resolves to:
/breeze/clienthistory/Metadata
Your server-side Web API query methods can expect db as an optional parameter:
[HttpGet]
public string Metadata(string db="")
{
... do what is right ...
}
Saves?
I assume that you also want to identify the target database when you save. There are lots of ways you can include that in the save request
in a custom HTTP header via a custom AJAX adapter (you could do this for queries too)
in a query string parameter or hash segment on the saveChanges POST request URL (again via a custom AJAX adapter).
in the tag property of the saveOptions object (easily accessed by the server implementation of SaveChanges)
in the resourceName property of the saveOptions object (see "named save")
You'll want to explore this variety of options on your own to find the best choice for you.

Configure Autofac Container for background thread

I have an asp.net MVC site which has many components registered using an InstancePerHttpRequest scope, however I also have a "background task" which will run every few hours which will not have an httpcontext.
I would like to get an instance of my IRepository which has been registered like this
builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>))
.InstancePerHttpRequest();
How do I do this from a non http context using Autofac? I think the IRepository should use the InstancePerLifetimeScope
There are several ways of how you can do that:
The best one in my opinion. You can register the repository as InstancePerLifetimeScope as you said. It works with HttpRequests and LifetimeScopes equally well.
builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>))
.InstancePerLifetimeScope();
Your registration for HttpRequest may differ from registration for LifetimeScope, then you can have two separate registrations:
builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>))
.WithParameter(...)
.InstancePerHttpRequest(); // will be resolved per HttpRequest
builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>))
.InstancePerLifetimeScope(); // will be resolved per LifetimeScope
You can explicitly create "HttpRequest" scope using its tag. Exposed through MatchingScopeLifetimeTags.RequestLifetimeScopeTag property in new versions.
using (var httpRequestScope = container.BeginLifetimeScope("httpRequest")) // or "AutofacWebRequest" for MVC4/5 integrations
{
var repository = httpRequestScope.Resolve<IRepository<Entity>>();
}

Resources