I'm trying to upgrade the latest Autofac package to 5.2.0, but not really successfully becasue of interface changes,
From (Autofac 4.9.4)
public static class ResolutionExtensions
{
public static bool TryResolve<T>(this IComponentContext context, out T instance);
}
To (Autofac 5.2.0)
public static class ResolutionExtensions
{
public static bool TryResolve<T>(this IComponentContext context, out T instance)
where T : class;
}
The ServiceStack package has a IContainerAdapter interface (ServiceStack.Interfaces 5.8.0)
public interface IResolver
{
T TryResolve<T>();
}
public interface IContainerAdapter : IResolver
{
T Resolve<T>();
}
My AutofacIocAdapter implementates this IContainerAdapter
public class AutofacIocAdapter : IContainerAdapter
{
public T TryResolve<T>()
{
if (m_Container.TryResolve<Autofac.ILifetimeScope>(out var scope) &&
scope.TryResolve<T>(out var scopeComponent))
{
return scopeComponent;
}
if (m_Container.TryResolve<T>(out var component))
{
return component;
}
return default(T);
}
}
But got compiling error after upgrading Autofac
Error CS0452 The type 'T' must be a reference type in order to use it as parameter 'T' in the generic type or method 'ResolutionExtensions.TryResolve<T>(IComponentContext, out T?)'
Any suggestion to resolve ?
You wouldn't be able to call a class with a class constraint from a method without that constraint from C#, but you could use reflection to invoke it.
But your first attempt should be to bypass the API with the constraint. Looking at AutoFac's implementation of TryResolve will show what APIs they call internally:
public static bool TryResolve<T>(this IComponentContext context, out T? instance)
where T : class
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
object? component;
// Null annotation attributes only work if placed directly in an if statement.
if (context.TryResolve(typeof(T), out component))
{
instance = (T)component;
return true;
}
else
{
instance = default;
return false;
}
}
So you would just need to bypass their generic API with constraints and call the same runtime Type API that they call, something like:
public class AutofacIocAdapter : IContainerAdapter
{
private readonly Autofac.IContainer container;
public AutofacIocAdapter(Autofac.IContainer container) =>
this.container = container;
public T TryResolve<T>()
{
if (container.TryResolve<Autofac.ILifetimeScope>(out var scope) &&
scope.TryResolve(typeof(T), out var scopeComponent))
return (T)scopeComponent;
if (container.TryResolve(typeof(T), out var component))
return (T)component;
return default;
}
public T Resolve<T>()
{
var ret = TryResolve<T>();
return !ret.Equals(default)
? ret
: throw new Exception($"Error trying to resolve '{typeof(T).Name}'");
}
}
Related
I have a service and ILogger that I need to use in my TypeConverter but the constructor with the parameters isn't being used or fired and my logger and service remain null when converting the type.
public class ModelServiceVersionConverter : TypeConverter
{
private static readonly Type StringType = typeof(string);
private static readonly Type VersionType = typeof(ModelServiceVersion);
private readonly ModelServiceVersions modelServiceVersions;
private readonly ILogger<ModelServiceVersionConverter> logger;
public ModelServiceVersionConverter()
{
}
public ModelServiceVersionConverter(ModelServiceVersions modelServiceVersions, ILogger<ModelServiceVersionConverter> logger)
{
this.modelServiceVersions = modelServiceVersions;
this.logger = logger;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) =>
sourceType == StringType ||
sourceType == VersionType ||
base.CanConvertFrom(context, sourceType);
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is null)
{
return this.modelServiceVersions.ServiceVersions.Last();
}
var modelServiceVersion = this.modelServiceVersions.ServiceVersions.FirstOrDefault(x => x == value.ToString());
if (modelServiceVersion is null)
{
var errorMessage = $"Version {value} unexpected. No implementation of {nameof(IModelService)} for this version.";
this.logger.LogError(errorMessage);
throw new ArgumentException(errorMessage);
}
return modelServiceVersion;
}
}
The ModelServiceVersion class is really simple and has the TypeConverter attribute on it.
[TypeConverter(typeof(ModelServiceVersionConverter))]
public class ModelServiceVersion : ValueObject, IComparer<ModelServiceVersion>
{
private readonly string version;
public static ModelServiceVersion New(string version)
{
return new ModelServiceVersion(version);
}
private ModelServiceVersion(string version) =>
this.version = version;
public static implicit operator string(ModelServiceVersion modelServiceVersion) => modelServiceVersion.version;
public static implicit operator ModelServiceVersion(StringValues stringValues) => New(stringValues[0]);
protected override IEnumerable<object> GetEqualityComponents()
{
yield return this.version;
}
public int Compare(ModelServiceVersion x, ModelServiceVersion y)
{
// TODO these rules can and will likely change
var versionNumberX = FirstOrDefaultDigit(x?.version);
var versionNumberY = FirstOrDefaultDigit(x?.version);
if (y is null || versionNumberY is null)
{
return 1;
}
if (x is null || versionNumberX is null)
{
return -1;
}
if (versionNumberX == versionNumberY)
{
return 0;
}
return versionNumberX > versionNumberY ? 1 : -1;
static int? FirstOrDefaultDigit(string versionString) =>
versionString?.ToCharArray().FirstOrDefault(IsDigit);
}
}
The services are all registered with Microsoft.Extensions.DependencyInjection.IServiceCollection.
I have tried just registering as scoped but it's not firing the type converter constructor to inject the dependencies in.
services.AddScoped<ModelServiceVersionConverter>();
When being used in the action on the controller
public async Task<IActionResult> GetPrediction([FromRoute] int decisionModelId, [FromQuery] ModelServiceVersion version, [FromBody] GetPredictionModelRequest.Request request)
{
var result = await this.predictionModelQueries.GetPrediction(new GetPredictionModelRequest(decisionModelId, request));
return this.Ok(result);
}
Any ideas where I'm going wrong?
Any help greatly appreciated.
Let's assume you have created a custom Attribute and applied it to a method or a class. Will constructor DI work in this case?
No. This is not supported unless you use specific asp.net-core attributes like ServiceFilterAttribute.
So you need to redesign your classes or have static access to DI container IServiceProvider.
Lets say that I have a code:
public class Test {
private readonly IFactory _factory;
private readonly ISomeClass _someClass;
public Test(IFactory factory, ISomeClass someClass)
{
_factory = factory;
_someClass = someClass;
}
....
public void TestMethod() {
_someClass.Do(_factory.CreateSomeObject());
}
}
public class Factory {
public SomeObject CreateSomeObject() {
return new SomeObject();
}
}
public class SomeClass {
public void Do(SomeObject obj){
....
}
}
I would like to get return type of CreateSomeObject from InvocationExpressionSyntax of someClass.Do(_factory.CreateSomeObject()); Is it possible?
I have a list of arguments (ArgumentSyntax) but I have no clue how to get method return type from ArgumentSyntax.
Is there better and easier way to do it other then scanning a solution for Factory class and analyzing CreateSomeObject method?
Yes, it is possible.
You would need to use Microsoft.CodeAnalysis.SemanticModel for it.
I assume you have CSharpCompilation and SyntaxTree already available, so you would go in your case with something like this:
SemanticModel model = compilation.GetSemanticModel(tree);
var methodSyntax = tree.GetRoot().DescendantNodes()
.OfType<MethodDeclarationSyntax>()
.FirstOrDefault(x => x.Identifier.Text == "TestMethod");
var memberAccessSyntax = methodSyntax.DescendantNodes()
.OfType<MemberAccessExpressionSyntax>()
.FirstOrDefault(x => x.Name.Identifier.Text == "CreateSomeObject");
var accessSymbol = model.GetSymbolInfo(memberAccessSyntax);
IMethodSymbol methodSymbol = (Microsoft.CodeAnalysis.IMethodSymbol)accessSymbol.Symbol;
ITypeSymbol returnType = methodSymbol.ReturnType;
Once you get the desired SyntaxNode out of the semantic model, you need to get its SymbolInfo and properly cast it, to get its ReturnType which does the trick.
I have the following code defined in terms of hierarchy -
public interface ISomeInterface
{
bool DoSomething();
}
public abstract class AbsActualWorker : ISomeInterface
{
public bool DoSomething()
{
Console.WriteLine("DoSomething");
throw new Exception("throwing exception for the sake of it!");
}
public abstract bool DoSomething2();
}
public class ActualWorker : AbsActualWorker
{
public override bool DoSomething2()
{
Console.WriteLine("DoSomething2");
Thread.Sleep(1000);
return true;
//throw new Exception("throwing exception for the sake of it!");
}
}
Im trying to resolve to the ActualWorker level and execute its DoSomething2
var container = new UnityContainer();
container.AddNewExtension<Interception>();
container.RegisterType<AbsActualWorker, ActualWorker>();
container
.RegisterType<ISomeInterface, ActualWorker>(new Interceptor(new InterfaceInterceptor()),
new InterceptionBehavior(new MyLoggerBehavior())
);
var instance = container.Resolve<ISomeInterface>();
if (instance != null)
{
instance.DoSomething();
}
Code happily resolves and can call
instance.DoSomething();
When I cast instance to ActualWorker Im getting null. Id like to use call DoSomething2.
public class MyLoggerBehavior : IInterceptionBehavior
{
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
var returnValue = getNext()(input, getNext);
if (returnValue.Exception != null)
{
Console.WriteLine("Exception occurred!!");
}
else
{
Console.WriteLine("Method {0} returned {1}", input.MethodBase, returnValue.ReturnValue);
}
return returnValue;
}
public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
public bool WillExecute
{
get { return true; }
}
}
Your problem is in interceptor. Due to
container.RegisterType<ISomeInterface, ActualWorker>(new Interceptor(new InterfaceInterceptor()), new InterceptionBehavior(new MyLoggerBehavior()));
You get for ISomeInterface not ActualWorker type but a wrappertype that realize ISomeInterface. This type cannot be casted to ActualWorker.
If you work with Dependency injection you should not call methods that is not in public interface that you inject. If you need to cast variable of interface to concrete realization it means that you do something wrong.
I have a series of interface definitions, all of which compile (so my objects are composed correctly). The objects instantiate, as expected. However, when I try to return the object from its' underlying factory I get the following error:
ERROR:
Unable to cast object of type
'SampleLibrary.Domain.DataAcessors.Person.SQLDataAccessor' to type
'Common.Contracts.DataAccessors.IDataAccessorModel`2[SampleLibrary.Contracts.Models.IPerson,SampleLibrary.Domain.DataAccessors.Types.SqlServer]'.
Please keep in mind I am trying to return each instance as the IDataAccessor interface.
CODE:
public interface IDataAccessor<I, T>
{
T AccessType { get; }
}
public interface IDataAccessorModel<I, T> : IDataAccessor<I, T>
{
I Instance { get; }
IResult<string> Get(I instance);
IResult<string> Add(I instance);
IResult<string> Update(I instance);
IResult<string> Delete(I instance);
}
public class SQLDataAccessor : IDataAccessorModel<IPerson, IAccessType>
{
internal SQLDataAccessor(IResult<string> result)
{
_connectionString = "";
_result = result;
}
private readonly string _connectionString;
private IResult<string> _result;
public IAccessType AccessType { get { return new SqlServer(); } }
public IPerson Instance { get; private set; }
public IResult<string> Add(IPerson instance)
{
Instance = instance;
return _result;
}
public IResult<string> Get(IPerson instance)
{
Instance = instance;
return _result;
}
public IResult<string> Delete(IPerson instance)
{
Instance = instance;
return _result;
}
public IResult<string> Update(IPerson instance)
{
Instance = instance;
return _result;
}
}
public class FactoryDataAccess : IFactoryDataAccess
{
internal FactoryDataAccess() { }
public IDataAccessor<I, T> Create<I, T>()
{
var model = typeof(I);
var target = typeof(T);
if (model.IsAssignableFrom(typeof(IPerson)))
{
if (target == typeof(SqlServer)) {
var accessor = new Person.SQLDataAccessor(new Result());
// This next line FAILS!
return (IDataAccessorModel<I, T>)accessor;
}
}
throw new NotSupportedException("Type " + target.FullName + " and Source " + model.FullName + " is not supported.");
}
}
UPDATE:
Please keep in mind that IDataAccessorModel can be used by any desired DataAccess type you wish to define.
SQLDataAccessor implements IDataAccessorModel<IPerson, IAccessType>, so it would work only if <I, T> were <IPerson, IAccessType>. There is no guarantee about that, since the method is generic and I and T could be any type, so the cast fails.
Of course, since you're checking the types of I and T, you know the cast would be valid, but the compiler doesn't. You can trick it like this:
return (IDataAccessorModel<I, T>)(object)accessor;
However, since T has to be SqlServer, it doesn't make sense to make it a generic type parameter. And since I has to implement IPerson, there should be a constraint on it. So the method signature should be:
public IDataAccessor<I, T> Create<T>() where T : IPerson
It work for me if I declare SQLDataAccessor like this:
public class SQLDataAccessor : IDataAccessorModel<IPerson, SqlServer>
{
...
}
You are probably calling it like this
var factory = new FactoryDataAccess();
var da = factory.Create<IPerson, SqlServer>();
i.e. you call it with T being SqlServer. If you declare T as IAccessType in SQLDataAccessor it is not guaranteed that IAccessType would be SqlServer. Therefore the casting error. (However, SqlServer is guaranteed to be IAccessType as it probably implements it.)
SQLDataAccessor is not a generic class, but implements IDataAccessorModel<IPerson, IAccessType> exactly, so your method Create should return IDataAccessor<IPerson, IAccessType>, but you probably called it with other generic types.
Change SqlDataAccessor to:
public class SQLDataAccessor<I, T> : IDataAccessorModel<I, T>
{
internal SQLDataAccessor(IResult<string> result)
{
_connectionString = "";
_result = result;
}
private readonly string _connectionString;
private IResult<string> _result;
public T AccessType { get { return new SqlServer(); } }
public I Instance { get; private set; }
public IResult<string> Add(I instance)
{
Instance = instance;
return _result;
}
public IResult<string> Get(I instance)
{
Instance = instance;
return _result;
}
public IResult<string> Delete(I instance)
{
Instance = instance;
return _result;
}
public IResult<string> Update(I instance)
{
Instance = instance;
return _result;
}
}
You might want to limit I and T to the interfaces, so add a where constraint:
public class SQLDataAccessor<I, T> : IDataAccessorModel<I, T>
where I : IPerson
where T : IAccessType
The way you have it, I could be any type derived from IPerson and T is exactly of type SqlServer, which would cause the cast to fail since SQLDataAccessor implements the IDataAccessorModel with different parameters. You would need to have a more exact cast, such as:
return (IDataAccessorModel<IPerson, IAccessType>)accessor;
Lately I'm playing around a bit with the IoC tool LightCore. Since the last version no named instances are allowed anymore nontheless the ServiceLocator pattern is supported(?).
One of the crucial functionalities I need from the ServiceLocator is the resolving of named instances. My first idea was to override the DoGetInstance method and implement my own code.
My last workaround was to get a instance by it's typename
_testInstanceKey = "My.Namespace.MyType, MyAssembly";
IMyType type = locator.GetInstance(typeof(IMyType), _testInstanceKey)
protected override object DoGetInstance(Type serviceType, string key)
{
return _container.ResolveAll(serviceType)
.Where(x => x.GetType() == Type.GetType(key,true,true))
.FirstOrDefault();
}
This works out but what do I need a ServiceLocator for if I hardwire my typename?!
Has anybody any suggestions how I could work around this half-baked ServiceLocator?
You can solve this problem by introducing a IMyTypeFactory interface and letting the application take a dependency on that interface:
// Factory interface:
public interface IMyTypeFactory
{
IMyType GetByName(string name);
}
// Implementation in the composition root:
public class MyTypeFactory :
Dictionary<string, Func<IRequestHandler>>, IMyTypeFactory
{
public IMyType GetByName(string name) { return this[name](); }
}
// Registration
var factory = new MyTypeFactory
{
{ "foo", () => new MyType1() },
{ "bar", () => new MyType2() },
{ "boo", () => new MyType3() },
};
builder.Register<IMyTypeFactory>(c => factory);
As I said in the comments, try to move away from the use of the Service Locator anti-pattern. It will improve the testability and maintainability of your application. When using the dependency injection (DI) pattern, you don't call the container directly and there is no way to directly ask a named instance. When applying DI, you will have to change the way to configure the container or need to apply the right abstractions in your application (as shown with the IMyTypeFactory).
Assuming you can't change the way the framework calls the locator and assuming the framework uses the Common Service Locator is used as interface between your container and itself, you can add this functionality to a custom IServiceLocator implementation:
// This adapter wraps the CSL LightCoreAdapter of LightCore itself.
public class LightCoreServiceLocatorAdapter : IServiceLocator
{
private readonly LightCoreAdapter container;
public LightCoreServiceLocatorAdapter(IContainer container)
{
// You need a reference to LightCore.CommonServiceLocator.dll.
this.container = new LightCoreAdapter(container);
}
public IEnumerable<TService> GetAllInstances<TService>()
{
return this.container.GetAllInstances<TService>();
}
public IEnumerable<object> GetAllInstances(Type serviceType)
{
return this.container.GetAllInstances(serviceType);
}
public TService GetInstance<TService>(string key)
{
if (key == null)
{
return this.container.GetInstance<TService>(null);
}
else
{
// This is custom logic
this.container.GetInstance<INamedFactory<TService>>().GetByKey(key);
}
}
public TService GetInstance<TService>()
{
return this.container.GetInstance<TService>();
}
public object GetInstance(Type serviceType, string key)
{
if (key == null)
{
return this.container.GetInstance(serviceType);
}
else
{
// This is custom logic
var facType = typeof(INamedFactory<>).MakeGenericType(serviceType);
var factory = (INamedFactory)this.container.GetInstance(facType);
return factory.GetByKey(key);
}
}
public object GetInstance(Type serviceType)
{
return this.container.GetInstance(serviceType);
}
object IServiceProvider.GetService(Type serviceType)
{
((IServiceProvider)this.container).GetService(serviceType);
}
}
This class uses the following two custom interfaces:
public interface INamedFactory
{
object GetByKey(string key);
}
public interface INamedFactory<T> : INamedFactory
{
T GetByKey(string key);
}
Using this custom LightCoreServiceLocatorAdapter and these two custom interfaces, you can create custom factories, such as this generic one:
public sealed class NamedDelegateFactory<T> : INamedFactory<T>
{
private readonly Func<string, T> factory;
public NamedDelegateFactory(Func<string, T> factory)
{
this.factory = factory;
}
public T GetByKey(string key)
{
return this.factory(key);
}
object INamedFactory.GetByKey(string key)
{
return this.factory(key);
}
}
Which can be registered in the container as follows:
var factory = new NamedDelegateFactory<IMyType>(key =>
{
if (key == "Cool") return new MyType1();
else return new MyType2();
});
var builder = new ContainerBuilder();
builder.Register<INamedFactory<IMyType>>(c => factory);
Next, a LightCoreServiceLocatorAdapter can be created as follows:
var adapter = new LightCoreServiceLocatorAdapter(builder.Build());
Microsoft.Practices.ServiceLocation.ServiceLocator
.SetLocatorProvider(() => adapter);
You can register all named instances using the INamedFactory<T> interface and use the NamedDelegateFactory<T> wrapped with a delegate or implement a custom type that implements INamedFactory<T>.