Is it possible to create an object with parameters for each Resolve in a dependency registered in transient? #30
-
I am currently interested in Pure.DI for its excellent performance. Is it possible to create an object with parameters for each Resolve in a dependency registered in transient? Below is the code I would like to use as follows:
|
Beta Was this translation helpful? Give feedback.
Replies: 13 comments 7 replies
-
@YoshihiroIto thank you for your interest in this project. The problem you have raised is quite well known. The point is that there is no easy way to use the constructor injection together with the manual state passing there. Some tricks are always required. The simplest solution is to leave the constructor to DI, and use properties or a special method, for example called Initialize, to pass the state manually. In this case the object will not be fully initialized for some short time. Another option is to use a special "factory" class, which will create objects. In order not to create such an "factory" class, you can use System.Func<> instead as in the example below: using Pure.DI;
var composition = new Composition();
DI.Setup("Composition")
.Bind<IDependency>().To<Dependency>()
.Bind<Func<string, int, IService>>().As(Lifetime.Singleton).To<Func<string, int, IService>>(ctx => (param1, param2) =>
{
ctx.Inject<IDependency>(out var dep1);
ctx.Inject<IDependency>(out var dep2);
return new Service(dep1, dep2, param1, param2);
})
.Root<Func<string, int, IService>>("CreateService");
var factory = composition.Resolve<Func<string, int, IService>>();
var service1 = factory("ABC", 123); // <---(A)
var service2 = factory("XYZ", 789); // <---(B)
// But better is just:
var service3 = composition.CreateService("ABC", 123);
interface IDependency { }
class Dependency : IDependency { }
interface IService
{
public IDependency Dependency1 { get; }
public IDependency Dependency2 { get; }
public string Param1 { get; }
public int Param2 { get; }
}
class Service : IService
{
public Service(
IDependency dependency1,
IDependency dependency2,
string param1, // <----(1)
int param2) // <----(2)
{
Dependency1 = dependency1;
Dependency2 = dependency2;
Param1 = param1;
Param2 = param2;
}
public IDependency Dependency1 { get; }
public IDependency Dependency2 { get; }
public string Param1 { get; }
public int Param2 { get; }
} In this case, the code is still effective: public Func<string, int, IService> CreateService
{
get
{
if (Object.ReferenceEquals(_singletonM08D06di23, null))
{
lock (_disposableSingletonsM08D06di)
{
if (Object.ReferenceEquals(_singletonM08D06di23, null))
{
_singletonM08D06di23 = (param1, param2) =>
{
return new Service(new Dependency(), new Dependency(), param1, param2);
};
}
}
}
return _singletonM08D06di23;
}
} |
Beta Was this translation helpful? Give feedback.
-
Thank you for your answer. I appreciate your solution as well as the knowledge that this problem is well known. I will try to use the "factory" class. Here is what I would like to do.
I worry that this part needs to be declared for each class. For example, This is a mechanism for composition generation, but we would like to be able to declare it this way. Thank you. |
Beta Was this translation helpful? Give feedback.
-
This sample essentially prepends external singleton objects to the generated classes. You really need "external PerResolve" objects. Some time ago I thought of making similar arguments for each root of a composition. But unfortunately it would not work well for Resolve methods. Thanks for giving me the idea, it's an important use case after all. I'll try to figure out how to do it. Take a look at this example, but it's also quite workable: using Pure.DI;
var composition = new Composition();
var service1 = composition.CreateService("ABC", 123); // <---(A)
var service2 = composition.CreateService("XYZ", 789); // <---(B)
partial class Composition
{
[ThreadStatic] private static string _param1;
[ThreadStatic] private static int _param2;
private static void Setup() =>
DI.Setup(nameof(Composition))
.Bind<IDependency>().To<Dependency>()
.Bind<string>("param1").To(_ => _param1)
.Bind<int>("param2").To(_ => _param2)
.Bind<IService>().To<Service>()
.Root<IService>("ServiceRoot");
public IService CreateService(string param1, int param2)
{
_param1 = param1;
_param2 = param2;
return ServiceRoot;
}
}
interface IDependency { }
class Dependency : IDependency { }
interface IService
{
public IDependency Dependency1 { get; }
public IDependency Dependency2 { get; }
public string Param1 { get; }
public int Param2 { get; }
}
class Service : IService
{
public Service(
IDependency dependency1,
IDependency dependency2,
[Tag("param1")] string param1, // <----(1)
[Tag("param2")] int param2) // <----(2)
{
Dependency1 = dependency1;
Dependency2 = dependency2;
Param1 = param1;
Param2 = param2;
}
public IDependency Dependency1 { get; }
public IDependency Dependency2 { get; }
public string Param1 { get; }
public int Param2 { get; }
} |
Beta Was this translation helpful? Give feedback.
-
I have published a beta package and added the using Pure.DI;
var composition = new Composition();
var service1 = composition.CreateService(param1: "ABC", param2: 123); // <---(A)
var service2 = composition.CreateService(param1: "XYZ", param2: 789); // <---(B)
DI.Setup("Composition")
.Bind<IDependency>().To<Dependency>()
.RootArg<string>("param1")
.RootArg<int>("param2")
.Bind<IService>().To<Service>().Root<IService>("CreateService");
interface IDependency { }
class Dependency : IDependency { }
interface IService
{
public IDependency Dependency1 { get; }
public IDependency Dependency2 { get; }
public string Param1 { get; }
public int Param2 { get; }
}
class Service : IService
{
public Service(
IDependency dependency1,
IDependency dependency2,
string param1, // <----(1)
int param2) // <----(2)
{
Dependency1 = dependency1;
Dependency2 = dependency2;
Param1 = param1;
Param2 = param2;
}
public IDependency Dependency1 { get; }
public IDependency Dependency2 { get; }
public string Param1 { get; }
public int Param2 { get; }
} Please try it and tell me how convenient this API is. |
Beta Was this translation helpful? Give feedback.
-
Your work is excellent. I modified the sample slightly and tried it out. What if I need to have multiple service creators and each service has a different parameter type but the same name? I tried the following code. And it worked. Is this a correct usage of the Pure.DI specification?
|
Beta Was this translation helpful? Give feedback.
-
I'm glad it worked for your scenario. You used the API correctly. But another case is interesting when you have 2 different arguments of the same type. Then it is recommended to use tags to specify the correspondence, for example: using Pure.DI;
using System.Diagnostics;
var composition = new Composition();
var service1 = composition.CreateService(param1: "ABC", param2: 123, param3: "aaaa"); // <---(A)
var service2 = composition.CreateService(param1: "XYZ", param2: 789, param3: "bbbb"); // <---(B)
var otherService1 = composition.CreateOtherService(param1: new object(), param2: typeof(int));
var otherService2 = composition.CreateOtherService(param1: "vvv", param2: typeof(float));
Debug.WriteLine(otherService1);
DI.Setup("Composition")
.Bind<IDependency>().To<Dependency>()
.RootArg<string>("param1")
.RootArg<int>("param2")
.Bind<IService>().To<Service>().Root<IService>("CreateService")
.RootArg<object>("param1")
.RootArg<Type>("param2")
.Bind<IOtherService>().To<OtherService>().Root<IOtherService>("CreateOtherService")
.RootArg<string>("param3", "another string");
interface IDependency { }
class Dependency : IDependency { }
interface IService
{
public IDependency Dependency1 { get; }
public IDependency Dependency2 { get; }
public string Param1 { get; }
public int Param2 { get; }
}
class Service : IService
{
public Service(
IDependency dependency1,
IDependency dependency2,
string param1, // <----(1)
int param2, // <----(2)
[Tag("another string")] string param3) // <----(3)
{
Dependency1 = dependency1;
Dependency2 = dependency2;
Param1 = param1;
Param2 = param2;
}
public IDependency Dependency1 { get; }
public IDependency Dependency2 { get; }
public string Param1 { get; }
public int Param2 { get; }
}
interface IOtherService
{
object Param1 { get; }
Type Param2 { get; }
}
class OtherService : IOtherService
{
public OtherService(
object param1,
Type param2)
{
Param1 = param1;
Param2 = param2;
}
public object Param1 { get; }
public Type Param2 { get; }
} Tags allow you to work on the same type, but distinguish between them guided by custom logic, please pay attention to the constructor parameter |
Beta Was this translation helpful? Give feedback.
-
Thank you for responding to my many requests. Is it possible to specify the relationship between arguments and route names by attributes? There is an advantage to having the class definition and the relationship between the constructor arguments in one place. Also, I have had some experience in the past when I developed an application with many developers participating, and I had a conflict problem in the part of registering classes to a DI container. In the case of Pure.DI.
The advantage is that registered classes can be seen at a glance, but because they are concentrated in one place, it was difficult to avoid conflicts when many developers participate. The following is my imagination.
|
Beta Was this translation helpful? Give feedback.
-
Unfortunately attributes are not supported right now. They have too many limitations. But it is possible in the future. But even now you can split the DI setup to avoid conflicts as in the example below: using Pure.DI;
using System.Diagnostics;
var composition = new Composition();
var service1 = composition.CreateService(param1: "ABC", param2: 123, param3: "aaaa"); // <---(A)
var service2 = composition.CreateService(param1: "XYZ", param2: 789, param3: "bbbb"); // <---(B)
var otherService1 = composition.CreateOtherService(param1: new object(), param2: typeof(int));
var otherService2 = composition.CreateOtherService(param1: "vvv", param2: typeof(float));
Debug.WriteLine(otherService1);
interface IDependency { }
class Dependency : IDependency
{
private void Setup() =>
DI.Setup("Composition")
.Bind<IDependency>().To<Dependency>();
}
interface IService
{
public IDependency Dependency1 { get; }
public IDependency Dependency2 { get; }
public string Param1 { get; }
public int Param2 { get; }
}
class Service : IService
{
public Service(
IDependency dependency1,
IDependency dependency2,
string param1, // <----(1)
int param2, // <----(2)
[Tag("another string")] string param3) // <----(3)
{
Dependency1 = dependency1;
Dependency2 = dependency2;
Param1 = param1;
Param2 = param2;
}
public IDependency Dependency1 { get; }
public IDependency Dependency2 { get; }
public string Param1 { get; }
public int Param2 { get; }
private void Setup() =>
DI.Setup("Composition")
.RootArg<string>("param1")
.RootArg<int>("param2")
.RootArg<string>("param3", "another string")
.Bind<IService>().To<Service>()
.Root<IService>("CreateService");
}
interface IOtherService
{
object Param1 { get; }
Type Param2 { get; }
}
class OtherService : IOtherService
{
public OtherService(
object param1,
Type param2)
{
Param1 = param1;
Param2 = param2;
}
public object Param1 { get; }
public Type Param2 { get; }
private void Setup() =>
DI.Setup("Composition")
.RootArg<object>("param1")
.RootArg<Type>("param2")
.Bind<IOtherService>().To<OtherService>()
.Root<IOtherService>("CreateOtherService");
} |
Beta Was this translation helpful? Give feedback.
-
I understand attribute is not currently supported. I look forward to your future work. And thank you for your help in presenting specific ways to avoid conflicts. |
Beta Was this translation helpful? Give feedback.
-
Hi! @NikolayPianikov It has been a long period of time, I am currently using your Pure.DI to develop my application. Thank you very much. I am having trouble with the RootArg that you previously told us about in this discussion, as I am getting errors in some cases. I have made some changes to the sample code I used before. The following code does not generate the correct CreateService method and results in a compile error. I think the cause is my lack of understanding. Please let me know what is wrong with this code. using Pure.DI;
using System.Diagnostics;
var composition = new Composition();
var service1 = composition.CreateService(param1: "ABC", param2: 123); // <---(A)
var service2 = composition.CreateService(param1: "XYZ", param2: 789); // <---(B)
var otherService1 = composition.CreateOtherService(param3: new object(), param4: typeof(int), param5: service1);
var otherService2 = composition.CreateOtherService(param3: "vvv", param4: typeof(float), param5: service2);
Debug.WriteLine(otherService1);
DI.Setup("Composition")
.Bind<IDependency>().To<Dependency>()
.RootArg<string>("param1")
.RootArg<int>("param2")
.Bind<IService>().To<Service>().Root<IService>("CreateService")
.RootArg<object>("param3")
.RootArg<Type>("param4")
.RootArg<IService>("param5")
.Bind<IOtherService>().To<OtherService>().Root<IOtherService>("CreateOtherService");
interface IDependency
{
}
class Dependency : IDependency
{
}
interface IService
{
public IDependency Dependency1 { get; }
public IDependency Dependency2 { get; }
public string Param1 { get; }
public int Param2 { get; }
}
class Service : IService
{
public Service(
IDependency dependency1,
IDependency dependency2,
string param1,
int param2)
{
Dependency1 = dependency1;
Dependency2 = dependency2;
Param1 = param1;
Param2 = param2;
}
public IDependency Dependency1 { get; }
public IDependency Dependency2 { get; }
public string Param1 { get; }
public int Param2 { get; }
}
interface IOtherService
{
object Param3 { get; }
Type Param4 { get; }
IService Param5 { get; }
}
class OtherService : IOtherService
{
public OtherService(
object param3,
Type param4,
IService param5)
{
Param3 = param3;
Param4 = param4;
Param5 = param5;
}
public object Param3 { get; }
public Type Param4 { get; }
public IService Param5 { get; }
} |
Beta Was this translation helpful? Give feedback.
-
Thank you for presenting the sample code. The generated code from your sample creates a new IService instance in CreateOtherService. This is the source code I would like you to generate. Is it possible to achieve this result? #region Composition Roots
[global::System.Runtime.CompilerServices.MethodImpl((global::System.Runtime.CompilerServices.MethodImplOptions)0x300)]
public IService CreateService(string param1, int param2)
{
var transientM11D18di2 = new Dependency();
var transientM11D18di1 = new Dependency();
var transientM11D18di0 = new Service(transientM11D18di1, transientM11D18di2, param1, param2);
return transientM11D18di0;
}
[global::System.Runtime.CompilerServices.MethodImpl((global::System.Runtime.CompilerServices.MethodImplOptions)0x300)]
public IOtherService CreateOtherService(System.Type param4, object param3, IService param5)
{
var transientM11D18di0 = new OtherService(param3, param4, param5);
return transientM11D18di0;
}
#endregion
|
Beta Was this translation helpful? Give feedback.
-
.Bind<IService>("root only").To<Service>() Defines the binding of an IService to a Service with the tag "root only". Where the binding key is actually (IService, "root only"). .Root<IService>("CreateService", "root only") Creates a root named "CreateService" that relies on the binding (IService, "root only"). Therefore, the |
Beta Was this translation helpful? Give feedback.
-
I didn't get the point of the problem with Arg. If it is relevant, you could send a sample of your code and expected behavior. Arg should work like RootArg, only the arguments are passed through the class constructor and are available in all roots. For unstance: using Pure.DI;
var composition = new Composition(param2: 123);
var service1 = composition.CreateService(param1: "ABC"); // <---(A)
var service2 = composition.CreateService(param1: "XYZ"); // <---(B)
Console.WriteLine(service1);
Console.WriteLine(service2);
var otherService1 = composition.CreateOtherService(param3: new object(), param4: typeof(string), param5: service1);
var otherService2 = composition.CreateOtherService(param3: new object(), param4: typeof(double), param5: service2);
Console.WriteLine(otherService1);
Console.WriteLine(otherService2);
Console.WriteLine(composition);
DI.Setup("Composition")
.Bind<IDependency>().To<Dependency>()
.RootArg<string>("param1")
.Arg<int>("param2")
.Bind<IService>("root only").To<Service>()
.Root<IService>("CreateService", "root only")
.RootArg<object>("param3")
.RootArg<Type>("param4")
.RootArg<IService>("param5")
.Bind<IOtherService>("also root-only").To<OtherService>()
.Root<IOtherService>("CreateOtherService", "also root-only");
interface IDependency
{
}
class Dependency : IDependency
{
}
interface IService
{
public IDependency Dependency1 { get; }
public IDependency Dependency2 { get; }
public string Text { get; }
public int Id { get; }
}
record Service(
IDependency Dependency1,
IDependency Dependency2,
string Text,
int Id) : IService;
interface IOtherService
{
object MyObject { get; }
Type SomeType { get; }
IService MyService { get; }
}
record OtherService(
object MyObject,
Type SomeType,
IService MyService): IOtherService; |
Beta Was this translation helpful? Give feedback.
I have published a beta package and added the
RootArg
method. Please see the example here. Now you can create composition roots with arguments. And your example can be rewritten like this: