I have a provider for a controller that is build using providers of repositories as follow:
final salesControllerProvider = FutureProvider
.autoDispose<SalesController>((ref) async {
var saleRepositoryProvider =
ref.watch(repositories.saleRepositoryProvider);
var branchRepositoryProvider =
ref.watch(repositories.branchRepositoryProvider);
var productRepositoryProvider =
ref.watch(repositories.productRepositoryProvider);
return SalesController(saleRepositoryProvider, detailRepositoryProviders,
branchRepositoryProvider, productRepositoryProvider);
});
Hence:
class SalesController {
AsyncValue<SaleRepository> saleRepositoryProvider;
AsyncValue<BranchRepository> branchRepositoryProvider;
AsyncValue<ProductRepository> productRepositoryProvider;
SalesController(
this.saleRepositoryProvider,
this.saleDetailRepositoryProviders,
this.branchRepositoryProvider,
this.productRepositoryProvider);
Later I use the providers in methods using, for example, branchRepositoryProvider.when(...). However, some of this repositories do not change often, so I was wondering if precalculating the information needed by the controller would be fine. For example,
late List<int> branchIds;
SalesController(
this.saleRepositoryProvider,
this.saleDetailRepositoryProviders,
this.branchRepositoryProvider,
this.productRepositoryProvider) {
branchIds = branchRepositoryProvider.when(
data: (repository) => repository.ids.toList(),
loading: () => <int>[],
...);
}
so that I would use branchRepositoryProvider.when(...) only once, if the list of branch identifiers was the only information needed from that repository, even though if it is used in several methods.
Is that a correct way of using future providers? Are there any issues with it?
Related
Here's what I'm doing so far (code simplified):
public class MyRegistrationSource : IRegistrationSource
{
public MyRegistrationSource(ContainerBuilder builder /*,...*/)
{
// ...
this.builder = builder;
}
public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
// Some checks here
var interfaceType = serviceWithType.ServiceType;
var implementorType = FindTheRightImplementor(interfaceType);
if (myRegisterConditionSatisfied)
{
return Register(implementorType, interfaceType);
}
return Empty;
}
private IEnumerable<IComponentRegistration> Register(Type concrete, Type #interface)
{
var regBuilder = builder.RegisterType(concrete).As(#interface).IfNotRegistered(#interface);
return new[] { regBuilder.CreateRegistration() };
}
}
Then, at startup I'm doing something like
builder.RegisterSource(
new NonRegisteredServicesRegistrationSource(builder/*, ...*/));
The above is intended to register those matching services only when there's no previous registration. I tried doing the registration without using the ContainerBuilder but couldn't get it to work.
This is working but are there any issues in passing-in the ContainerBuilder instance to the RegistrationSource?
Thanks!
I'd probably argue against passing in a ContainerBuilder.
Every type you register in your source will add a callback to a list of callbacks inside the Container Builder which will never get cleared, potentially creating a memory leak.
I'd suggest calling the static method RegistrationBuilder.ForType instead, which will give you a fluent builder and should let you subsequently call CreateRegistration as you are now.
You can see some pretty good examples of how do this in our Moq integration:
var reg = RegistrationBuilder.ForType(concrete)
.As(#interface)
.CreateRegistration();
Also, I don't believe IfNotRegistered will have any effect when used outside the context of a ContainerBuilder. You should use the provided registrationAccessor parameter to the registration source to look up a TypedService to see if it has already been registered:
var isRegistered = registrationAccessor(new TypedService(#interface)).Any();
Lets say I have a factory returning different classes via methods.
class CarFactory
{
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function createCarOne() : CarInterface
{
return $this->container->make(CarOneClass::class);
}
// Vs
public function createCarTwo() : CarInterface
{
return new CarTwoClass({Inject Dependencies Here});
}
}
When would this be considered a service locator or anti-pattern and why? I am considering the first method solely for the dependency resolution provided by the container. All car's have the same typed interface dependencies the main difference of the entities come from how they transform the data provided.
Whenever one of these methods are called I need a new instance of the specified car so the data set can be transformed based on the choice.
This is not the implementation but the easiest example I can provide.
$output = [];
foreach ($car as $key => $data) {
$newCar = $this->factory->createCar{$key}();
// Pass Some Data To The New Car Methods So It Can Be Transformed
$output[] = $newCar;
}
return $output;
If this is the wrong approach what would be the alternative option?
Edit
After further digging I see some IoC containers pass factory callables as dependencies. I was going to bind each Car to a callable but thanks to the ability to type hint data from method returns (php7) I can configure factories using a provider then call the 'callable factory' from within the CarFactory. Requires additional binding but prevents the need to reference/dependency inject the IoC container within every factory.
Still researching I would love to hear feedback from those with more experience.
Ex:
// Within Some Registered Provider
// I Will Have To Wire Each Car
$one = function() use ($app) {
return $app->make(CarOne::class);
};
$two = function() use ($app) {
return $app->make(CarTwo::class);
};
$app->bind(ICarFactory::class, function($app) use ($one, $two) {
return $app->make($concrete, [$one, $two]);
});
// Car Factory Constructor
public function __construct(callable $carOne, callable $carTwo) {
$this->one = $carOne;
$this->two = $carTwo;
}
Since get methods are type hinted ( view original car factory ) an error is thrown when the returned item does not implement CarInterface, each factory method would just have to call the 'callable factory' ( something like this return ($this->one)();).
I believe i solve my problem of outsourcing creation of dependencies ( avoiding creating within factory was bothering the hell out of me ) while still following 'best practices'. Still looking for advice if anyone has any to offer.
I've tried drilling down into the object and looking at the docs but haven't found anything. I've created an entity and I need to assign some properties manually. I see _backingStore and entityAspect on the object... and I know the property names but don't know how to set them via the breeze entity.
In case it matters, I'm creating a new object and then copying properties over from another object to facilitate cloning.
function createDocument() {
var manager = datacontext.manager;
var ds = datacontext.serviceName;
if (!manager.metadataStore.hasMetadataFor(ds)) {
manager.fetchMetadata(ds).then(function () {
return manager.createEntity("Document");
})
}
else {
return manager.createEntity("Document");
}
}
function cloneDocument(doc) {
var clonedDocument = createDocument();
// Copy Properties Here - how?
saveChanges()
.fail(cloneFailed)
.fin(cloneSucceeded);
}
Not knowing what your properties might be, here are two scenarios -
function cloneDocument(doc) {
var clonedDocument = createDocument();
clonedDocument.docId(doc.docId());
clonedDocument.description(doc.description());
saveChanges()
.fail(cloneFailed)
.fin(cloneSucceeded);
}
There are a few things to note here - I am assuming you are using Knockout and needing to set the properties. If you are not using Knockout then you can remove the parans and use equals -
clonedDocument.docId = doc.docId;
I believe this is true for if you are not using Knockout (vanilla js) and if you are using Angular, but I have not used Breeze with Angular yet so bear with me.
And here's another way that works regardless of model library (Angular or KO)
function cloneDocument(doc) {
var manager = doc.entityAspect.entityManager; // get it from the source
// Check this out! I'm using an object initializer!
var clonedDocument = manager.createEntity("Document", {
description: doc.description,
foo: doc.foo,
bar: doc.bar,
baz: doc.baz
});
return clonedDocument;
}
But beware of this:
clonedDocument.docId = doc.docId; // Probably won't work!
Two entities of the same type in the same manager cannot have the same key.
Extra credit: write a utility that copies the properties of one entity to another without copying entityAspect or the key (the id) and optionally clones the entities of a dependent navigation (e.g., the order line items of an order).
I have a SPA application (durandaljs), and I have a specific route where I map the "id" of the entity that I want to fetch.
The template is "/#/todoDetail/:id".
For example, "/#/todoDetail/232" or "/#/todoDetail/19".
On the activate function of viewmodel, I get the route info so I can grab the id. Then I create a new instance of breezejs EntityManager to get the entity with the given id.
The problem is when I call manager.fetchEntityByKey("Todos", id), the EntityManager doesn't have yet the metadata from the server, so it throwing exception "Unable to locate an 'Type' by the name: Todos".
It only works if first I execute a query against the store (manager.executeQuery), prior to calling fetchEntityByKey.
Is this an expected behavior or a bug ? Is there any way to auto-fecth the metadata during instantiation of EntityManager ?
note: I believe it's hard to use a shared EntityManager in my case, because I want to allow the user directly type the route on the browser.
EDIT: As a temporary workaround, I'm doing this:
BreezeService.prototype.get = function (id, callback) {
var self = this;
function queryFailed(error) {
app.showMessage(error.message);
callback({});
}
/* first checking if metadatastore was already loaded */
if (self.manager.metadataStore.isEmpty()) {
return self.manager.fetchMetadata()
.then(function (rawMetadata) {
return executeQuery();
}).fail(queryFailed);
} else {
return executeQuery();
}
/* Now I can fetch */
function executeQuery() {
return self.manager.fetchEntityByKey(self.entityType, id, true)
.then(callback)
.fail(queryFailed);
}
};
You've learned about fetchMetadata. That's important. If you application can begin without issuing a query, you have to use fetchMetadata and wait for it to return before you can perform any operations directly on the cache (e.g., checking for an entity by key in the cache before falling back to a database query).
But I sense something else going on because you mentioned multiple managers. By default a new manager doesn't know the metadata from any other manager. But did you know that you can share a single metadataStore among managers? You can.
What I often do (and you'll see it in the metadata tests in the DocCode sample), is get a metadataStore for the application, write an EntityManager factory function that creates new managers with that metadataStore, and then use the factory whenever I'm making new managers ... as you seem to be doing when you spin up a ViewModel to review the TodoDetail.
Coming from a Silverlight background where I used a lot of WCF RIA Services combined with Caliburn Micro, I used this approach for integrating Breeze with Durandal.
I created a sub folder called services in the App folder of the application. In that folder I created a javascript file called datacontext.js. Here is a subset of my datacontext:
define(function (require) {
var breeze = require('lib/breeze'); // path to breeze
var app = require('durandal/app'); // path to durandal
breeze.NamingConvention.camelCase.setAsDefault();
// service name is route to the Web API controller
var serviceName = 'api/TeamData',
// manager is the service gateway and cache holder
manager = new breeze.EntityManager(serviceName),
store = manager.metadataStore;
function queryFailed(error) {
app.showMessage("Query failed: " + error.message);
}
// constructor overrides here
// included one example query here
return datacontext = {
getSponsors: function (queryCompleted) {
var query = breeze.EntityQuery.from("Sponsors");
return manager
.executeQuery(query)
.then(queryCompleted)
.fail(queryFailed)
}
};
}
Then in your durandal view models you can just require the services/datacontext. For example, here is part of a sample view model from my app:
define(function (require) {
var datacontext = require('services/datacontext');
var ctor = function () {
this.displayName = 'Sponsors',
this.sponsors = ko.observable(false)
};
ctor.prototype.activate = function () {
var that = this;
return datacontext.getSponsors(function (data) { that.sponsors(data.results) });
}
return ctor;
});
This will allow you to not worry about initializing the metadata store in every view model since it is all done in one place.
I'm currently trying out Autofac in a new ASP.NET MVC project after having used Ninject, Castle Windsor and other IoC containers in the last years. So while I know about IoC containers in general, I'm fairly new to Autofac and I'm still looking for some best practices.
Currently I'm trying to find out if there is a way to resolve the innermost nested scope.
I have the following situation: a component that is registered as SingleInstance() has a method that creates a nested lifetime scope, providing a configuration action to configure some components as InstancePerLifetimeScope, and within this nested scope resolves the registered components to do something useful, like so:
ILifetimeScope currentScope = ???;
using (var scope = currentScope.BeginLifetimeScope(cb => {
cb.RegisterType<X>().InstancePerLifetimeScope();
// ...
}))
{
var comp = scope.Resolve<X>();
// ...
}
The issue is that I would like currentScope to be the innermost lifetime scope, because I know that X depends on components inside the innermost scope. In the simplest case that would be e.g. the current request lifetime scope. I can of course get it with AutofacDependencyResolver.Current.RequestLifetimeScope but I don't want to use that as it isn't really well testable. Also, that lifetime scope isn't necessarily the innermost.
So, is there a way to find the innermost lifetime scope given e.g. the root container or a different ILifetimeScope?
In Autofac, the innermost scope is always the container. Using the AutofacDependencyResolver, it'd be
AutofacDependencyResolver.Current.ApplicationContainer
There is no way from a nested scope (if all you have is an ILifetimeScope) to "walk backward" to get to the container. I'm not necessarily sure you want to do that, anyway.
It sounds like your SingleInstance component is doing some sort of service location, basically, with manual registration/resolution of certain components. If the set of types being registered is fixed, I might recommend (if possible) some redesign of your system, so the SingleInstance component isn't registered as SingleInstance anymore and instead gets registered as InstancePerDependency, then have that take these other items in as constructor parameters.
Instead of...
// Consuming class like this...
public class BigComponent
{
public void DoSomethingCool()
{
using(var scope = ...)
{
var c = scope.Resolve<SubComponent>();
c.DoWork();
}
}
}
// ...and container registrations like this...
builder.RegisterType<BigComponent>().SingleInstance();
You might try inverting it a bit:
// Consuming class like this...
public class BigComponent
{
private SubComponent _c;
public BigComponent(SubComponent c)
{
_c = c;
}
public void DoSomethingCool()
{
_c.DoWork();
}
}
// ...and container registrations like this...
builder.RegisterType<BigComponent>().InstancePerDependency();
builder.RegisterType<SubComponent>().InstancePerLifetimeScope();
The idea is to not have to do the on-the-fly registration-and-immediate-resolution thing.
If you're stuck doing service location, you'll need to use AutofacDependencyResolver.Current.ApplicationContainer if you need the absolute innermost scope, but keep in mind any objects you register scoped to InstancePerHttpRequest will not be resolvable if you do that, so you could get into trouble. It really is recommended to use the AutofacDependencyResolver.Current.RequestLifetimeScope instead. That would make your method:
var requestScope = AutofacDependencyResolver.Current.RequestLifetimeScope;
using (var scope = requestScope.BeginLifetimeScope(cb => {
cb.RegisterType<X>().InstancePerLifetimeScope();
// ...
}))
{
var comp = scope.Resolve<X>();
// ...
}
In a testing environment, the AutofacDependencyResolver lets you swap in the provider that dictates how request lifetimes get generated. You can implement a simple/stub one like this:
public class TestLifetimeScopeProvider : ILifetimeScopeProvider
{
readonly ILifetimeScope _container;
private ILifetimeScope _lifetimeScope = null;
public TestLifetimeScopeProvider(ILifetimeScope container)
{
if (container == null) throw new ArgumentNullException("container");
_container = container;
}
public ILifetimeScope ApplicationContainer
{
get { return _container; }
}
public ILifetimeScope GetLifetimeScope()
{
if (_lifetimeScope == null)
{
_lifetimeScope = ApplicationContainer.BeginLifetimeScope("httpRequest")
}
return _lifetimeScope;
}
public void EndLifetimeScope()
{
if (_lifetimeScope != null)
_lifetimeScope.Dispose();
}
}
Again, just a stub for unit testing, not something you'd ever use in production.
Then when you wire up the DependencyResolver in your test, you provide your lifetime scope provider:
var lsProvider = new TestLifetimeScopeProvider(container);
var resolver = new AutofacDependencyResolver(container, lsProvider);
DependencyResolver.SetResolver(resolver);
This lets you use InstancePerHttpRequest and such inside unit tests without actually having a real request context. It also means you should be able to use the request lifetime scope in your registration/resolution method and not have to fall back on the application container.
For those who are searching for ASP.NET WebApi:
You can use GetRequestLifetimeScope() method of AutofacWebApiDependencyResolver.