Get_It Flutter Multiple Abstract Class Registration - flutter

We are using the package get_it:^7.1.3 for dependency injection.
I want to register some classes with the same abstract class (like they were interfaces) and get them like:
List<AbstractLass> allClassesWhichImplementAbstractClass=di.sl.get<AbstractLass>();
Therefore I'd like to register it the following way:
sl.registerLazySingleton<AbstractLass>(() =>
AbstractLassImpl1());
sl.registerLazySingleton<AbstractLass>(() =>
AbstractLassImpl2());
But as I do that, I'll get an exception :
ArgumentError (Invalid argument(s): Object/factory with type AbstractLass is already registered inside GetIt. )
There is another solution where I register just the implementation the following way:
sl.registerLazySingleton<AbstractLassImpl1>(() =>
AbstractLassImpl1());
sl.registerLazySingleton<AbstractLassImpl2>(() =>
AbstractLassImpl2());
which works but then I would not access both by using their abstract class (if that is possible)
Can someone help me?

You can do it by providing instanceName parameter like so:
sl.registerLazySingleton<AbstractClass>(() =>
AbstractClassImpl1(), instanceName: 'implementation1');
sl.registerLazySingleton<AbstractClass>(() =>
AbstractClassImpl2(), instanceName: 'implementation2');
And use it also with the same name included:
sl.get<AbstractClass>(instanceName: 'implementation1');

You can only register one object using registerLazySingleton() due to its Singleton behaviour.
If you want to register multiple interfaces with different implementation, try using registerFactory(), etc.

Related

Run function with Toast in Application Class

Thank you all so much! I just started in Kotlin which probably should be called the K language (like C and F), and have found so many solutions here on this site...it's awesome!
I have an independent class file called AppTime.kt and it's declared in the AndroidManifest.xml file:
<application
android:name=".AppTime"
class AppTime : Application() {
fun burntToast(sMsg: String) {
Toast.makeText(this.applicationContext, "!", Toast.LENGTH_LONG).show()
}
}
It doesn't run when called anywhere from a Fragment class:
class FirstFragment : Fragment() {...
AppTime().burntToast()
I've tried every approach using parameters for the Toast following makeText(...
and then to call it from a Fragment with or without context or string parameters.
Is it the type of class I have?
Functions defined inside a class can only be called on an instance of that class, as you already found.
But you cannot simply instantiate an arbitrary Application and expect it to work. Android does a lot of behind-the-scenes setup of framework classes before they are usable. Any Application or Activity that you instantiate yourself is useless. You have to use the instances that are provided to you through the lifecycle of the Activities that get launched in your application.
If you want to call this function from your Fragment, you will have to get an instance of your application, which you can get from its associated Activity. Since the Activity class doesn't know about your specific subclass of Application, you must also cast the application to your specific subclass to be able to call its unique functions. You can get the Activity by using requireActivity().
(requireActivity().application as AppTime).burntToast()

The type 'FluentValidation.IValidator' is not an open generic class

On an ASP.NET Core application I am registering
services.Scan(x => x.FromAssembliesOf(typeof(Startup))
.AddClasses(y => y.AssignableTo(typeof(IValidator)))
.AsImplementedInterfaces()
.WithScopedLifetime());
I tried to replicate this using Autofac so I used:
builder
.RegisterAssemblyTypes(typeof(Startup).Assembly)
.AsClosedTypesOf(typeof(IValidator))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
But I got the following error:
Unhandled Exception: System.ArgumentException:
The type 'FluentValidation.IValidator' is not an open generic class or interface type so it won't work with methods that act on open generics.
What am I doing wrong?
Instead of AsClosedTypesOf use a Where clause to filter out and register just the types that implement IValidator. AsClosedTypesOf is specifically there to support open generics. There are plenty of examples in the Autofac docs to help you out.

Registering a type with both EnableClassInterceptors and WithParameter

I'm having an issue with Autofac where it seems like EnableClassInterceptors is interfering with my ability to use .WithParameter(...). When the constructor is being called on Service using the code below, someString is not being populated. Notes:
I've tried using ResolvedParameter instead, it does not help (note: my Resolved parameter still includes the name of the parameter when I tried that)
If I remove EnableClassInterceptors and InterceptedBy, the parameter does get populated properly. This, however, isn't a valid solution as I need the interceptors.
Re-ordering WithParameter, EnableClassInterceptors, and InterceptedBy does not help.
Looking at Type Interceptors, specifically the "Class Interceptors and UsingConstructor" section, on docs.autofac.org, it mentions that using EnableClassInterceptors will cause ConstructUsing to fail. I think something similar might be happening with my scenario below.
Snippet of my registration code looks like this:
var builder = new ContainerBuilder();
builder.RegisterType<Dependency>.As<IDependency>.InstancePerLifetimeScope();
builder.RegisterType<Service>()
.As<IService>()
.WithParameter(new NamedParameter("someString", "TEST"))
.EnableClassInterceptors()
.InterceptedBy(typeof(LogExceptionsInterceptor));
Service's constructor looks something like this:
public class Service : IService
{
public Service(IDependency dependency, string someString)
{
if(dependency == null)
throw ArgumentNullException(nameof(dependency));
if(someString == null)
//**throws here**
throw ArgumentNullException(nameof(someString));
}
}
[Guess] What I'm thinking is happening is that when EnableClassInterceptors is called, a proxy class is generated with a constructor that works on top of the existing one, but the parameter names do not copy over into the proxy class/constructor.
Is this a problem? Is there a way to form the registration that allows both WithParameter and EnableClassInterceptors to be used together? Is it a bug in Autofac?
Your guess is correct: the generated proxy class does not keep the constructor parameter names.
Currently there is no way to influence this in DynamicProxy so this is not a bug of Autofac (although this edge case currently not documented on the Autofac documentation website).
This is how your original Service class's parameters look like:
typeof(Service).GetConstructors()[0].GetParameters()
{System.Reflection.ParameterInfo[2]}
[0]: {ConsoleApplication10.IDependency dependency}
[1]: {System.String someString}
But the generated proxy does not keep the names:
GetType().GetConstructors()[0].GetParameters()
{System.Reflection.ParameterInfo[3]}
[0]: {Castle.DynamicProxy.IInterceptor[] }
[1]: {ConsoleApplication10.IDependency }
[2]: {System.String }
So you have two not very robust options to workaround this limitation with WithParameter:
use the TypedParamter with string as the type:
.WithParameter(new TypedParameter(typeof(string), "TEST"))
However if you have multiple paramters with the same type this won't work
use the PositionalParameter in this case you need to add 1 if the type is proxied
.WithParameter(new PositionalParameter(2, "TEST"))
Another options would be to don't use a primitive string type but create a wrapper e.g. MyServiceParameter or create another service which can provide these string configuration values to your other services.

How do I register an interface to the EntityFramework.UserStore<TUser> constructor?

I have been following this post to learn about DI with AutoFac. My DB context is registered in Startup.cs like this:
builder.RegisterType<MyDb>().As<IMyDbCtx>().InstancePerRequest();
I also followed the instructions to create the ApplicationUserStore class, and registered this type like so:
builder.RegisterType<ApplicationUserStore>().As<IUserStore<ApplicationUser>>().InstancePerRequest();
This was all fine until I realized that ApplicationUserStore is expecting a concrete DbContext in it's constructor to pass to its base constructor:
public ApplicationUserStore(MyDb context)
: base(context)
{
}
When I run the application and try loading the registration page, this is the error I see:
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'TestApp.Service.IdentityConfig.ApplicationUserStore' can be invoked with the available services and parameters:
Cannot resolve parameter 'TestApp.Data.MyDb context' of constructor 'Void .ctor(MyApp.Data.MyDb)'.
This is breaking, I assume, because I already set up MyDb as a IMyDbCtx, so it won't accept the concrete type. This is good because I don't want to be inconsistent by injecting abstractions in some spots and implementations in others.
Does anyone have any ideas on how I can pass in an IMyDbCtx to the UserStore ctor, particularly with AutoFac?
This is not an ideal solution, but I decided to cast the interface to the concrete type when the parameter is passed to the base constructor:
public ApplicationUserStore(IMyDbCtx context)
: base((MyDb)context)
{
}
You can register your type MyDb under as many aliases as you wish:
builder.RegisterType<MyDb>()
.As<MyDb>()
.As<IMyDbCtx>()
.InstancePerRequest();
In this case Autofac can resolve Application User Store. But it kills the whole idea of dependency injection, because you feed the concrete type.

How to carry out custom initialisation with autofac

I'm adding autofac to an existing project and some of the service implementations require their Initialize method to be called and passed configuration information. Currently I'm using the code:
builder.Register(context =>
{
var service =
new SqlTaxRateProvider(context.Resolve<IUserProvider>());
service.Initialize(config);
return service;
}
).As<ITaxService>()
.SingleInstance();
which works but I'm still creating the object myself which is what I'm trying to get away from this and allow autofac to handle it for me. Is it possible to configure a post create operation that would carry out the custom initialisation?
To give you an idea of what I'm after ideally this would be the code:
builder.RegisterType<SqlTaxRateProvider>()
.As<ITaxService>()
.OnCreated(service=> service.Initialize(config))
.SingleInstance();
Update:
I am using Autofac-2.1.10.754-NET35
.OnActivating(e => e.Instance.Initialize(...))
should do the trick.
You might also investigate the Startable module (see the Startable entry in the Autofac wiki).
Mark's suggestion to do initialisation in the constructor is also a good one. In that case use
.WithParameter(new NamedParameter("config", config))
to merge the config parameter in with the other constructor dependencies.