DryIoc 3.0 Castle Interceptor - dryioc

The recommended way to use interception in DryIoc doesn't work anymore since 3.0:
https://bitbucket.org/dadhi/dryioc/wiki/Interception
The Method "GetPublicInstanceConstructors" is missing.
What is the new way to achive this?

Made.Of((Type type) => type.GetConstructors().SingleOrDefault(c => c.GetParameters().Length != 0)
Seems to work.

Here is working integration code (which probably will be moved to separate package in future):
using System;
using Castle.DynamicProxy;
using ImTools;
namespace DryIoc.Interception
{
// Extension methods for interceptor registration using Castle Dynamic Proxy.
public static class DryIocInterception
{
public static void Intercept<TService, TInterceptor>(this IRegistrator registrator, object serviceKey = null)
where TInterceptor : class, IInterceptor =>
registrator.Intercept<TInterceptor>(typeof(TService), serviceKey);
public static void Intercept<TInterceptor>(this IRegistrator registrator, Type serviceType, object serviceKey = null)
where TInterceptor : class, IInterceptor =>
registrator.Intercept(serviceType, Parameters.Of.Type(typeof(IInterceptor[]), typeof(TInterceptor[])), serviceKey);
public static void Intercept(this IRegistrator registrator,
Type serviceType, ParameterSelector interceptorsParameterSelector, object serviceKey = null)
{
Type proxyType;
if (serviceType.IsInterface())
proxyType = ProxyBuilder.CreateInterfaceProxyTypeWithTargetInterface(
serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default);
else if (serviceType.IsClass())
proxyType = ProxyBuilder.CreateClassProxyTypeWithTarget(
serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default);
else
throw new ArgumentException(
$"Intercepted service type {serviceType} is not a supported, cause it is nor a class nor an interface");
registrator.Register(serviceType, proxyType,
made: Made.Of(
pt => pt.PublicConstructors().FindFirst(ctor => ctor.GetParameters().Length != 0),
interceptorsParameterSelector),
setup: Setup.DecoratorOf(useDecorateeReuse: true, decorateeServiceKey: serviceKey));
}
private static DefaultProxyBuilder ProxyBuilder => _proxyBuilder ?? (_proxyBuilder = new DefaultProxyBuilder());
private static DefaultProxyBuilder _proxyBuilder;
}
}

Related

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 test a Ninject Factory returns the same instance in RequestScope

In a WebApi application, I am using Ninject to inject a factory that allows consuming classes to create a DbContext that is scoped according to their requirements. I want to be able to write a unit test that verifies that two calls to the same factory for a Request scoped context return the same instance.
The factory interface is:
public interface IContextFactory
{
IMyContext GetContext();
IMyContext GetTransientContext();
}
The Ninject configuration looks like this:
Bind<IContextFactory>().ToFactory();
Bind<IMyContext>().To<MyContext>()
.InRequestScope()
.NamedLikeFactoryMethod((IContextFactory f) => f.GetContext());
Bind<IMyContext>().To<MyContext>()
.InTransientScope()
.NamedLikeFactoryMethod((IContextFactory f) => f.GetTransientContext());
My unit test is as follows:
[Fact]
public void RequestScopedContextAlwaysSame()
{
// Arrange
NinjectWebCommon.Start();
var factory = (IContextFactory)NinjectWebCommon.Bootstrapper.Kernel.GetService(typeof(IContextFactory));
//Act
var context1 = factory.GetContext();
var context2 = factory.GetContext();
//Assert
Assert.Same(context1, context2);
}
I expected the both calls to the factory to return the same instance, but in fact they are two different instances. I think this is a testing error as in the application, I have been able to successfully verify that the same instance is injected into different consumers when they call the GetContext() method.
I suspect this is not working in a unit test because there is no HttpContext and InRequestScope() depends on it. Is there a way round this?
I suspect this is not working in a unit test because there is no
HttpContext and InRequestScope() depends on it.
I think you are right. You may try :
Bind<IMyContext>().To<MyContext>()
.InScope(ctx => this.Scope)
.NamedLikeFactoryMethod((IContextFactory f) => f.GetContext());
where this.Scope is a property of your Test class (any reference type will be ok) the value of which is initialized upon your test setup
Alternatively, if you still want to use the InRequestScope syntax, you may try :
public class MyPlugin : INinjectHttpApplicationPlugin
{
private Object Scope { get; } = true;
public void Dispose(){}
public INinjectSettings Settings { get; set; }
public object GetRequestScope(IContext context)
{
return Scope;
}
public void Start() { }
public void Stop() { }
}
Then your test would be something like :
public void RequestScopedContextAlwaysSame()
{
var kernel = new StandardKernel();
kernel.Components.Add<INinjectHttpApplicationPlugin, MyPlugin>();
kernel.Bind<IContextFactory>().ToFactory();
kernel.Bind<IMyContext>().To<MyContext>()
.InRequestScope()
.NamedLikeFactoryMethod((IContextFactory f) => f.GetContext());
kernel.Bind<IMyContext>().To<MyContext>()
.InTransientScope()
.NamedLikeFactoryMethod((IContextFactory f) => f.GetTransientContext());
var factory = kernel.Get<IContextFactory>();
//Act
var context1 = factory.GetContext();
var context2 = factory.GetContext();
//Assert
Assert.IsTrue(context1.Equals(context2));
}

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.

Entity Manager in Service in Zend Framework 2

I have written a custom service for my module. This service provides public static functions which should validate a given token.
Now i want to implement another public static function which checks if an Doctrine-Entity exists or not. For this case i need the object-manager or the service-locator in my service.
class ApiService
{
const KEY_LENGTH = 10;
const USE_NUMBERS = true;
const USE_CHARS = true;
public static function isValid($apiKey) {
$isValid = false;
# more code tbd
$isValid = self::exists($apiKey);
return $isValid;
}
public static function exists($apiKey) {
# Insert Object-Manager here
$validator = new \DoctrineModule\Validator\ObjectExists(array(
'object_repository' => $objectManager->getRepository('Application\Entity\User'),
'fields' => array('email')
));
}
}
Is it "best-practice" to implement my functions as public static and call them as static methods?
What is the best practice to inject the object-manager into my doesEntityExist() function?
The best approach would be to completely remove the static methods from your class here. ZF2 makes it really easy to fetch services by their name, so you shouldn't really need static methods for such a use case.
First of all, clean up your service:
namespace MyApp\Service;
use Doctrine\Common\Persistence\ObjectRepository;
use DoctrineModule\Validator\ObjectExists;
class ApiService
{
// ...
protected $validator;
public function __construct(ObjectRepository $objectRepository)
{
$this->validator = new \DoctrineModule\Validator\ObjectExists(array(
'object_repository' => $objectRepository,
'fields' => array('email')
));
}
public function exists($apiKey)
{
return $this->validator->isValid($apiKey);
}
// ...
}
Now define a factory for it:
namespace MyApp\ServiceFactory;
use MyApp\Service\ApiService;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ApiServiceFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$entityManager = $serviceLocator->get('Doctrine\ORM\EntityManager');
$repository = $entityManager->getRepository('Application\Entity\User');
return new ApiService($repository);
}
}
Then map the service name to the factory (usually in your module):
namespace MyApp;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
class Module implements ConfigProviderInterface
{
public function getConfig()
{
return array(
'service_manager' => array(
'factories' => array(
'MyApp\Service\ApiService'
=> 'MyApp\ServiceFactory\ApiServiceFactory',
),
),
);
}
}
NOTE: you may want to simply use a closure instead of defining a separate factory class, but having factory classes gives you a small performance boost when you're not using the service. Also, using a closure in configuration means you cannot cache the merged configuration, so consider using the method suggested here.
Here's an example without the factory class (again, consider using the approach explained above):
namespace MyApp;
use Zend\ModuleManager\Feature\ServiceProviderInterface;
class Module implements ServiceProviderInterface
{
public function getServiceConfig()
{
return array(
'factories' => array(
'MyApp\Service\ApiService' => function ($sl) {
$entityManager = $serviceLocator->get('Doctrine\ORM\EntityManager');
$repository = $entityManager->getRepository('Application\Entity\User');
return new MyApp\Service\ApiService($repository);
},
),
);
}
}
Now you can use the service in your controllers:
class MyController extends AbstractActionController
{
// ...
public function apiAction()
{
$apiService = $this->getServiceLocator()->get('MyApp\Service\ApiService');
if ( ! $apiService->isValid($this->params('api-key')) {
throw new InvalidApiKeyException($this->params('api-key'));
}
// ...
}
// ...
}
You can also retrieve it wherever you have the service manager:
$validator = $serviceLocator->get('MyApp\Service\ApiService');
As an additional suggestion, consider simplifying your service. Since isValid is already a method of your validator, you could simply return the validator itself (hereby using the closure method for simplicity):
namespace MyApp;
use Zend\ModuleManager\Feature\ServiceProviderInterface;
use DoctrineModule\Validator\ObjectExists;
class Module implements ServiceProviderInterface
{
public function getServiceConfig()
{
return array(
'factories' => array(
'MyApp\Validator\ApiKeyValidator' => function ($sl) {
$entityManager = $serviceLocator->get('Doctrine\ORM\EntityManager');
$repository = $entityManager->getRepository('Application\Entity\User');
new ObjectExists(array(
'object_repository' => $objectRepository,
'fields' => array('email')
));
},
),
);
}
}
Personally, I'd make the service a 'service' and put it in the ServiceManager. In addition I'd consider refactoring the code. Right now you have a dependency on the ObjectExists validator, which in turn depends on and entity repository, and that depends on the entity manager. It would be much simpler to compose the validator outside the service and inject it from a factory. That way, if you ever need to use a different validator, you just hand it a different one.
class ApiService
{
protected $validator;
public function isValid($apiKey)
{
// other code
$isValid = $this->exists($apiKey);
}
public function exists($apiKey)
{
return $this->getValidator()->isValid($apiKey);
}
public function setValidator(\Zend\Validator\AbstractValidator $validator)
{
$this->validator = $validator;
return $this;
}
public function getValidator()
{
return $this->validator;
}
}
In Module.php create the service as a factory method, or better still as a factory class, but that's left as an exercise for you :)
public function getServiceConfig()
{
return array(
'factories' => array(
'ApiService' => function($sm) {
$em = $sm->get('Doctrine\ORM\EntityManager');
$repo = $em->getRepository('Application\Entity\User');
$validator = new \DoctrineModule\Validator\ObjectExists($repo,
array('fields' => array('email')));
$service = new ApiService();
$service->setValidator($validator);
return $service;
},
),
);
}
Now if you need a different EntityManager, a different Entity repository, or even a whole different validator you only need to change a couple of lines above rather than having to delve into your services code.