Registering simple types in a specific example - dryioc

Consider the following
ClassA has a constructor that takes an instance of MasterClass and a string and exposes a property called Names of type string[].
ClassB has a constructor that takes an IJuicePresser and a IEnumerable<string>.
ClassC has a constructor that takes an IEnumerable<string>.
Manually I would do something like this to tie them together.
var masterClass = new MasterClass();
var juicePresser = JuicePresser.Create("default");
var classA = new ClassA(masterClass, "string");
var names = classA.Names;
var classB = new ClassB(juicePresser, names as IEnumerable<string>);
var classC = new ClassC(Array.Reverse(names));
How can I set up DryIoc to handle these registrations/resolutions for me?

It might be better to move all these run-time names from constructors to corresponding methods. But here is the matching DryIoc setup as-is:
Live on .NET Fiddle
using System;
using System.Collections.Generic;
using System.Linq;
using DryIoc;
public class Program
{
public static void Main()
{
var c = new Container();
c.Register<MasterClass>();
c.Register<JuicePresser>(Made.Of(() => JuicePresser.Create("default")));
// an example how to inject a primitive value: "string" in this case
c.Register<ClassA>(made: Parameters.Of.Type<string>(_ => "string"));
// service key is optional, just to distinguish the list of strings for consumer.
c.Register<string[]>(
Made.Of(_ => ServiceInfo.Of<ClassA>(), factory => factory.Names),
serviceKey: "names");
// register reverse names using ReverseHelper method
c.Register<string[]>(
Made.Of(() => ReverseHelper(Arg.Of<string[]>("names"))),
serviceKey: "reverse-names");
// specify the names and required type (string[]) for injection
c.Register<ClassB>(made: Parameters.Of.Type<IEnumerable<string>>(typeof(string[]), serviceKey: "names"));
// specify reverse names for injection
c.Register<ClassC>(made: Parameters.Of.Type<string[]>(serviceKey: "reverse-names"));
var classB = c.Resolve<ClassB>();
var classC = c.Resolve<ClassC>();
Console.WriteLine(string.Join(" - ", classB.Names.ToArray()));
// outputs: a - string - z
Console.WriteLine(string.Join(" - ", classC.Names));
// outputs: z - string - a
}
public static T[] ReverseHelper<T>(T[] target) {
Array.Reverse(target);
return target;
}
public class MasterClass {}
public class JuicePresser
{
public readonly string Name;
private JuicePresser(string name) { Name = name; }
public static JuicePresser Create(string name)
{
return new JuicePresser(name);
}
}
public class ClassA
{
public readonly string[] Names;
public ClassA(MasterClass master, string name) {
Names = new[] { "a", name, "z" }; // for example
}
}
public class ClassB
{
public readonly JuicePresser Presser;
public readonly IEnumerable<string> Names;
public ClassB(JuicePresser presser, IEnumerable<string> names) {
Presser = presser;
Names = names;
}
}
public class ClassC
{
public readonly string[] Names;
public ClassC(string[] names) {
Names = names;
}
}
}
Update:
Explaining a bit the part with:
c.Register<string[]>(
Made.Of(_ => ServiceInfo.Of<ClassA>(), factory => factory.Names),
serviceKey: "names");
DryIoc has support for using not only constructors for service creation, but also static and instance methods (factory methods), properties and fields. Here is the wiki topic.
In example above we use Names property of ClassA object as factory method to register service type "string[]" with service key "names".
Let's see in detail:
// Registering service of ClassA. It means that it can be resolved / injected directly,
// or/and we can use it as a factory for resolving further services
c.Register<ClassA>(made: Parameters.Of.Type<string>(_ => "string"));
// Registering service of type "string[]"
c.Register<string[]>(
// Made.Of enables specifying factory method to use for "string[]" resolution, instead default constructor selection rules.
Made.Of(
// 1) Specifying what factory object should be used,
// Here we say to use ClassA service registered in container
requestNotUsedHere => ServiceInfo.Of<ClassA>(),
// 2) Specify that Names property of resolved ClassA object
// should be used for "string[]" resolution
classA => classA.Names),
// As the "string[]" is not very distinctive (unique) service type
// (you might register other "string[]" with different meaning),
// we identify the names with "names" service key. So when injected or
// resolved, you need to specify the service key in addition to type.
serviceKey: "names");
If you want to register with static factory method, property, field, then you don't need to specify request => ServiceInfo.Of<TFactory>() part. BTW, request parameter can be used for conditional selection of factory.

Related

Can't read variables from application.properties

I am making a Spring application in which I'm sending data to kinesis. I have the accessKey and secretKey stored in application.properties. In the service implementation class I am using these variables but im getting an error that access key cannot be null.
The ProducerServiceImpl class:
public class ProducerServiceImpl extends ProducerService {
private static final Logger LOG = LoggerFactory.getLogger(ProducerServiceImpl.class);
#Value(value = "${aws.stream_name}")
private String streamName;
#Value(value = "${aws.region}")
private String awsRegion;
#Value(value = "${aws.access_key}")
private String awsAccessKey;
#Value(value = "${aws.secret_key}")
private String awsSecretKey;
private KinesisProducer kinesisProducer = null;
public ProducerServiceImpl() {
this.kinesisProducer = getKinesisProducer();
}
private KinesisProducer getKinesisProducer() {
if (kinesisProducer == null) {
BasicAWSCredentials awsCreds = new BasicAWSCredentials(awsAccessKey, awsSecretKey);
KinesisProducerConfiguration config = new KinesisProducerConfiguration();
config.setRegion(awsRegion);
config.setCredentialsProvider(new AWSStaticCredentialsProvider(awsCreds));
config.setMaxConnections(1);
config.setRequestTimeout(6000); // 6 seconds
config.setRecordMaxBufferedTime(5000); // 5 seconds
kinesisProducer = new KinesisProducer(config);
}
return kinesisProducer;
}
I think the reason is that because after the function, the constructor is called first, the variables are not being assigned the value from #Value.
If you're using the #Value annotation on fields, Spring will use field injection. This means that it first needs to create an instance of ProducerServiceImpl (by calling your constructor), and then it will use reflection to initialize those fields annotated with #Value.
So, since your constructor is invoked before the values are injected, they will be null.
There are two basic solutions, one is to use constructor injection:
public class ProducerServiceImpl extends ProducerService {
// ... Fields
// Pass #Value as constructor parameters
public ProducerServiceImpl(#Value(value = "${aws.access_key}") String awsAccessKey) {
// Set the fields before calling getKinesisProducer()
// If you only need the fields to create the producer, you could also just pass them as arguments to the getKinesisProducer() function
this.awsAccessKey = awsAccessKey;
this.kinesisProducer = getKinesisProducer();
}
// ...
}
The other solution is to wait until Spring has initialized the bean. This can be done by moving the logic to a method annotated with #PostConstruct:
public class ProducerServiceImpl extends ProducerService {
// ... Fields
// Replace the constructor by a method annotated with #PostConstruct
#PostConstruct
public void initializeKinesisProducer() {
this.kinesisProducer = getKinesisProducer();
}
// ...
}
However, the prefered solution is to move the entire KinesisProducer setup to a Spring configuration class, and provide it as a bean. Since all Spring beans are singletons, you can get rid of that initialization code. For example:
// Create a new #Configuration class
#Configuration
public class KinesisConfiguration {
// Create a #Bean method to construct your KinesisProducer
#Bean
// Pass all #Value's as parameters
public KinesisProducer kinesisProducer(#Value("${aws.access_key} String awsAccessKey) {
BasicAWSCredentials awsCreds = new BasicAWSCredentials(awsAccessKey, awsSecretKey); KinesisProducerConfiguration config = new KinesisProducerConfiguration();
config.setRegion(awsRegion);
config.setCredentialsProvider(new AWSStaticCredentialsProvider(awsCreds));
config.setMaxConnections(1);
config.setRequestTimeout(6000); // 6 seconds
config.setRecordMaxBufferedTime(5000); // 5 seconds
return new KinesisProducer(config);
}
}
Now you can delete all that code in your service and use:
public class ProducerServiceImpl extends ProducerService {
// Inject your KinesisProducer, either through field injection or constructor injection
#Autowired
private KinesisProducer kinesisProducer;
}
I would suggest moving your #Value configuration properties to another class using #ConfigurationProperties and inject that in your constructor.

How do you resolve multiple dependencies using serviceKey to match parameter.name?

I am trying to figure out how to get DryIoc to resolve ITest on ExampleClass?
This is matching the parameter name to the service key as there are multiple registrations to locate the correct service.
public class Program
{
public void Main()
{
var container = new Container();
container.Register<ITest, A>(serviceKey: "a");
container.Register<ITest, B>(serviceKey: "b");
container.Register<ExampleClass>();
var example = container.Resolve<ExampleClass>();
}
}
public interface ITest { }
public class A : ITest { }
public class B : ITest { }
public class ExampleClass
{
public ExampleClass(ITest a, ITest b)
{
}
}
Use Parameters.Of https://www.fuget.org/packages/DryIoc.dll/4.2.5/lib/netstandard2.0/DryIoc.dll/DryIoc/Parameters
public class Program
{
public void Main()
{
var c = new Container();
c.Register<ITest, A>(serviceKey: "a");
c.Register<ITest, B>(serviceKey: "b");
c.Register<ExampleClass>(made:
Made.Of(parameters: Parameters.Of
.Name("a", serviceKey: "a")
.Name("b", serviceKey: "b")));
var example = c.Resolve<ExampleClass>();
}
}
You may also omit the Made.Of(parameters: because ParameterSelector returned by Parameters.Of is implicitly convertable to Made:
c.Register<ExampleClass>(made:
Parameters.Of
.Name("a", serviceKey: "a")
.Name("b", serviceKey: "b"));
You may apply more generic matching of parameter name to service key without explicitly listing the parameters, but it will be more fragile given you will add non-keyed parameter later:
c.Register<ExampleClass>(made:
Parameters.Of.Details(
(req, parInfo) => ServiceDetails.Of(serviceKey: parInfo.Name)));
Another type-safe option is directly specifying the constructor via delegate expression (Linq.Expressions.Expression<T>) describing its positional arguments - this option will inform you with compilation error when constructor is changed:
c.Register<ExampleClass>(made:
Made.Of(() =>
new ExampleClass(
Arg.Of<ITest>(serviceKey: "a"),
Arg.Of<ITest>(serviceKey: "b"))));
The above ways applied on the specific registration, but the same may be done on Container level using Rules:
var c = new Container(rules =>
rules.With(parameters:
Parameters.Of.Details(
(req, parInfo) => req.ServiceType == typeof(ExampleClass)
? ServiceDetails.Of(serviceKey: parInfo.Name)
: null)
));
Note: The last option affects performance because the rule need to be checked for all registrations.
The same approaches can be applied for specifying the property injection using PropertiesAndFields.Of.

How to register IEnumerable<IService> In DryIoc Mvc Controller like Autofac Enumeration (IEnumerable<B>, IList<B>, ICollection<B>)

Test Code With Autofac is Ok,but with DryIoc is error. How to make this work.
public class HomeController : Controller
{
private readonly ITestAppService _testAppService;
private readonly IEnumerable<ITestAppService> _testAppServiceList;
public HomeController(ITestAppService testAppService, IEnumerable<ITestAppService> testAppServiceList)
{
_testAppService = testAppService;
_testAppServiceList = testAppServiceList;
}
}
public class Test1AppService : ITestAppService{}
public class Test2AppService : ITestAppService{}
public interface ITestAppService: IAppService{}
The error 'Make sure that the controller has a parameterless public constructor ' is caused by field ITestAppService in controller not the field IEnumerable.Following is my register code.
var impls =
typeof(IAppService).Assembly
.GetTypes()
.Where(type =>
type.IsPublic &&
!type.IsAbstract &&
type.GetInterfaces().Length != 0 && typeof(IAppService).IsAssignableFrom(type));
foreach (var type in impls)
{
container.Register(type.GetInterfaces().First(), type, Reuse.Transient);
}
Resolved by dadhi's suggestion,My container is
DryIoc.IContainer container = new DryIoc.Container(
rules =>
{
return rules.WithFactorySelector(Rules.SelectLastRegisteredFactory())
.WithResolveIEnumerableAsLazyEnumerable();
}
);
The dryioc wiki about Resolving from multiple default services may need some update. Rules.SelectLastRegisteredFactory is a method.
The problem is that consdering two implementations of ITestAppService container doesn't know which to select for the first dependency. But you may explicitly define the convention:
var container = new Container(rules => rules
.WithFactorySelector(Rules.SelectLastRegisteredFactory));

Creating a fallback container/resolver DryIoc

Current working on creating a Prism.DryIoc.Forms project to try out DryIoc (first time!).
In Xamarin.Forms there is a native DependencyService and to provide a nice way to migrate towards using Prism I would like to add it as a fallback container in case the requsted service type can't be resolved from the main container.
Current I have created a FallbackContainer and pass the instance of IContainerand overrides the methods for IResolver and delegates the rest of the IContainer calls to the instance passed during creation.
So after the default container is created and configured and then do
Container = CreateContainer();
ConfigureContainer();
Container.Rules.WithFallbackContainer(new DependencyServiceContainer(Container));
Is this the preferred method or is there any way just to attach a default IResolver?
Current implementation
public class FallbackDependencyServiceContainer : IContainer
{
private readonly IContainer container;
public FallbackDependencyServiceContainer(IContainer container)
{
this.container = container;
}
public object Resolve(Type serviceType, bool ifUnresolvedReturnDefault)
{
return ResolveFromDependencyService(serviceType);
}
public object Resolve(Type serviceType, object serviceKey, bool ifUnresolvedReturnDefault,
Type requiredServiceType,
RequestInfo preResolveParent, IScope scope)
{
return ResolveFromDependencyService(serviceType);
}
public IEnumerable<object> ResolveMany(Type serviceType, object serviceKey, Type requiredServiceType,
object compositeParentKey,
Type compositeParentRequiredType, RequestInfo preResolveParent, IScope scope)
{
return new[] { ResolveFromDependencyService(serviceType) };
}
private static object ResolveFromDependencyService(Type targetType)
{
if (!targetType.GetTypeInfo().IsInterface)
{
return null;
}
var method = typeof(DependencyService).GetTypeInfo().GetDeclaredMethod("Get");
var genericMethod = method.MakeGenericMethod(targetType);
return genericMethod.Invoke(null, new object[] { DependencyFetchTarget.GlobalInstance });
}
....
}
Thanks and looking forward to test DryIoc since I've read it's supposed to be the fastest out there
Updated answer:
You may directly use WithUnknownServiceResolvers returning DelegateFactory:
var c = new Container(Rules.Default.WithUnknownServiceResolvers(request =>
new DelegateFactory(_ => GetFromDependencyService(request.ServiceType))));
No need to implement IContainer just for that.
I think it may be optimized regarding performance by replacing DelegateFactory with ExpressionFactory. But I need some time to play with the idea.

mock domain class created by another domain class with spock

I'm trying to test a domain class called EnityContact. Inside that class there is a method called initialize which populates some fields when needed. in order to do that the method creates instances of some other domain classes: AisUser, Entity and CPerson. AisUser is the domain class returned by the call to SecurityUtil.retrieveCurrentAisUser(false).
class EntityContact extends BaseObject implements Initializable{
....
#Override
void initialize() {
println "initaliazing"
isMain = false
creationDate = new Date()
createdBy = CPerson.get(SecurityUtil.retrieveCurrentAisUser(false).id)
entity = new Entity()
entity.setId(Long.valueOf(0)) //Id has to be initialized with some value
}
}
What i am trying to do is find a way to return mocks of those classes that i define in my specification.
Any ideas?
In Groovy you can mock static methods using MetaClass.
SecurityUtil.metaClass.'static'.retrieveCurrentAisUser = { boolean param ->
}