Is it possible to use ICacheManager<> with different type of configurations at the same time? - unity-container

Imagine that I have interfaces like below which all inherits from ICacheManager<>
public interface ICacheManagerRuntime<T> : ICacheManager<T>
public interface ICacheManagerRedis<T> : ICacheManager<T>
public interface ICacheManagerRedisWithRuntime<T> : ICacheManager<T>
I want to inject ICacheManager{CacheType} interfaces to implemantation of Cache classes like:
CacheRuntime, CacheRedis, CacheRedisWithRuntime
With unity I want to inject them like below:
container.RegisterType<ICacheManagerRuntime<object>>(
new ContainerControlledLifetimeManager(),
new InjectionFactory((c, t, n) =>
{
return CacheFactory.Build... // Return CacheManager just with RuntimeCacheHandle
})));
container.RegisterType<ICacheManagerRedis<object>>(
new ContainerControlledLifetimeManager(),
new InjectionFactory((c, t, n) =>
{
return CacheFactory.Build... // Return CacheManager just with RedisCacheHandle
})));
container.RegisterType<ICacheManagerRedisWithRuntime<object>>(
new ContainerControlledLifetimeManager(),
{
return CacheFactory.Build... // Return CacheManager just with RuntimeCacheHandleWithRedisBackPlane
})));
What ever I have done, I am getting this exception:
An unhandled exception of type 'Microsoft.Practices.Unity.ResolutionFailedException' occurred in Microsoft.Practices.Unity.dll
Additional information: Resolution of the dependency failed, type = "Solid.Play.Business.Interfaces.IProductService", name = "(none)".
Exception occurred while: Resolving parameter "cache" of constructor Solid.Play.Cache.Caches.CacheRuntime(Solid.Play.Cache.Interfaces.ICacheManagerRuntime`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] cache).
Exception is: InvalidCastException - Unable to cast object of type 'CacheManager.Core.BaseCacheManager`1[System.Object]' to type 'Solid.Play.Cache.Interfaces.ICacheManagerRuntime`1[System.Object]'.
-----------------------------------------------
At the time of the exception, the container was:
Resolving Solid.Play.Business.Services.ProductService,(none) (mapped from Solid.Play.Business.Interfaces.IProductService, (none))
Resolving Solid.Play.Cache.Interception.CachingInterceptorBehavior,(none)
Resolving parameter "cache" of constructor Solid.Play.Cache.Interception.CachingInterceptorBehavior(Solid.Play.Cache.Interfaces.ICacheSolid cache)
Resolving Solid.Play.Cache.Caches.CacheSolid,(none) (mapped from Solid.Play.Cache.Interfaces.ICacheSolid, (none))
Resolving parameter "cacheRuntime" of constructor Solid.Play.Cache.Caches.CacheSolid(Solid.Play.Cache.Interfaces.ICacheRuntime cacheRuntime, Solid.Play.Cache.Interfaces.ICacheRedis cacheRedis, Solid.Play.Cache.Interfaces.ICacheRedisWithRuntime cacheRedisWithRuntime)
Resolving Solid.Play.Cache.Caches.CacheRuntime,(none) (mapped from Solid.Play.Cache.Interfaces.ICacheRuntime, (none))
Resolving parameter "cache" of constructor Solid.Play.Cache.Caches.CacheRuntime(Solid.Play.Cache.Interfaces.ICacheManagerRuntime`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] cache)

The cast does not work because you are trying to cast an instance to an interface it does not implement.
Simplified, what you are trying looks like this:
public interface IBase
{
}
public interface ISub : IBase { }
public class BaseClass : IBase
{
}
var sub = (ISub)new BaseClass();
If you want to inject different kind of instances of the same interfaces, the Unity DI framework provides a way to do that via named injection.
Example:
container.RegisterType<ICacheManager<object>>("runtimeCache",
new ContainerControlledLifetimeManager(),
new InjectionFactory((c, t, n) =>
{
return CacheFactory.Build<object>(s =>
{
s.WithSystemRuntimeCacheHandle("cache.runtime");
});
}));
container.RegisterType<ICacheManager<object>>("redisCache",
new ContainerControlledLifetimeManager(),
new InjectionFactory((c, t, n) =>
{
return CacheFactory.Build<object>(s =>
{
s.WithRedisConfiguration("cache.redis", config =>
{
config
.WithAllowAdmin()
.WithDatabase(0)
.WithEndpoint("localhost", 6379);
})
.WithRedisCacheHandle("cache.redis");
});
}));
To resolve the first one, you'd use
var runtimeCache = container.Resolve<ICacheManager<object>>("runtimeCache");
You can inject the ICacheManager interface to constructors with attributes for example.
public YourClass([Dependency("runtimeCache")] ICacheManager<object> cache)
{
}

Related

How to inject nested IOptions with IServiceCollection.Configure

I have a Settings object that contains nested objects. I call WebApplicationBuilder.Configuration.Bind("Settings", options) to bind all the nested settings to the big Settings object, and I call IServiceCollection.Configure to make the big Settings object available through dependency injection. The only thing I don't like about this is that I have to inject the whole big object whenever I want to use it; I'm not able just to inject one of the nested objects. Can someone tell me what would be the best way to set things up so I can inject just one of the nested options objects if I need to?
public class Settings
{
public RepositoryOptions Repository { get; set; } = new();
public StoredProcedureOptions StoredProcedures { get; set; } = new();
...
}
Program.cs Main:
...
builder.Services.AddBLLServices(options =>
{
builder.Configuration.Bind("Settings", options);
options.Repository.IntegrationDatabaseConnectionString = builder.Configuration.GetConnectionString("Integration") ?? string.Empty;
options.StoredProcedures.MessageBrokerDatabaseConnectionString = builder.Configuration.GetConnectionString("MessageBroker") ?? string.Empty;
});
...
AddBLLServices method:
public static IServiceCollection AddBLLServices(this IServiceCollection services, Action<Settings> configureOptions)
{
// Make Settings object available through DI
services.Configure(configureOptions);
// Add services based on settings
var options = new Settings();
configureOptions.Invoke(options);
Configure(services, options);
return services;
}
Attempting to inject IConfig of a nested object injects an unconfigured object. Because I'm passing an Action to IServiceCollection.Configure I don't know how I could pull out one of the nested objects.
Edit:
Attempting to follow the advice of itsdaniel0 I have changed my AddBLLServices method to this to try to register the nested objects with DI:
public static IServiceCollection AddBLLServices(this IServiceCollection services, Action<Settings> configureOptions)
{
var options = new Settings();
configureOptions.Invoke(options);
services.Configure<Settings>(o => o = options);
services.Configure<RepositoryOptions>(o => o = options.Repository);
services.Configure<StoredProcedureOptions>(o => o = options.StoredProcedures);
services.Configure<WireTapListenerOptions>(o =>
{
var opt = new Settings();
configureOptions.Invoke(opt);
o = opt.WireTapListener;
});
services.Configure<WireTapQueueListenerOptions>(o => o = options.WireTapQueueListener);
services.Configure<ServiceListenerOptions>(o => o = options.ServiceListener);
services.Configure<EmailAlertOptions>(o => o = options.EmailAlert);
Configure(services, options);
return services;
}
But when I try to inject the options in a constructor the value is not configured.
Your original problem is because you're registering a single instance of the class, then inject the subclasses. You need to register them each individually.
Usually you would bind each class to a configuration section
services.Configure<SubClass>(config.Bind("Parent:Nested"));
In the position you're in, there is no built-in way.
I'd suggest the following extension method to register an instance
It's worth pointing out, this won't allow for the use of IOptionsSnapshot, but can be adapted to suit your needs as required
public static class IServiceCollectionExtensions
{
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, TOptions instance)
where TOptions : class
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
_ = services.AddOptions();
_ = services.AddSingleton<IOptions<TOptions>>(new OptionsWrapper<TOptions>(instance));
return services;
}
}
Then you can use it like so services.Configure(options.EmailAlert);

How to setup an execution strategy for Oracle in EF Core 2.2?

I want to set up the execution strategy for Oracle in Entity Framework Core 2.2 by using the DbContext injection. I am using NuGet package Oracle.EntityFrameworkCore for connecting to an Oracle DB.
services.AddDbContext<MyContext>(options =>
{
options.UseOracle(configuration.GetConnectionString("MyConnection"), oracleOptionsAction: oracleOptions =>
{
oracleOptions.ExecutionStrategy(x =>
new MyExecutionStrategy();
});
});
I have created the following execution strategy class for testing purposes. It turns out the class DbExecutionStrategy does not exist in dotnet core:
public class MyExecutionStrategy : DbExecutionStrategy
{
public MyExecutionStrategy() : base(10, TimeSpan.FromSeconds(30))
{
}
protected override bool ShouldRetryOn(Exception exception)
{
return true;
}
}
The class DbExecutionStrategy does not exist in dotnet core only in dotnet. You must implement the abstract class ExecutionStrategy instead.
The custom execution strategy class must be defined in this way:
public class MyExecutionStrategy : ExecutionStrategy
{
public MyExecutionStrategy(ExecutionStrategyDependencies dependencies, int maxRetryCount, TimeSpan maxRetryDelay)
: base(dependencies, maxRetryCount, maxRetryDelay)
{
}
protected override bool ShouldRetryOn(Exception exception)
{
return true;
}
}
Then the dependency injection section:
services.AddDbContext(options =>
{
options.UseOracle(configuration.GetConnectionString("MyConnection"), oracleOptionsAction: oracleOptions =>
{
oracleOptions.ExecutionStrategy(dependencies =>
new MyExecutionStrategy(
dependencies,
retryCount,
retryDelay));
});
});
For Oraсle it is preferable to use the OracleRetryingExecutionStrategy class of the Oracle.EntityFrameworkCore namespace. It already inherits from ExecutionStrategy and has an additional constructor parameter to which you can pass a collection of Oracle error codes to retry.
For example, here is an implementation of some class with default retry options and some Oracle code added for retry:
public class SomeRetryStrategy : OracleRetryingExecutionStrategy
{
private static readonly IList<int> ErrorNumbersToRetry = new List<int>()
{
28, // ORA-00028: Your session has been killed
12570 // ORA-12570: Network Session: Unexpected packet read error
};
public SomeRetryStrategy(ExecutionStrategyDependencies dependencies)
: base(dependencies, DefaultMaxRetryCount, DefaultMaxDelay, ErrorNumbersToRetry)
{
}
}

Type not found in cache: System.Func for injecting a type as a Func using MVVMLight SimpleIoc

I am registering a type in MVVMLight SimpleIoc,
SimpleIoc.Default.Register<MyInjectingClass>();
Then I do a constructor injection of this type as a Func,
public class MyConsumerClass
{
readonly Func<MyInjectingClass> _injectingClassFactory;
public MyConsumerClass(Func<MyInjectingClass> injectingClassFactory)
{
_injectingClassFactory = injectingClassFactory;
}
}
But at runtime, I get this error,
Type not found in cache:
System.Func`1[[<...>.MyInjectingClass,
<...>, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].
How can I constructor inject a type as a Func?
Note:
I'm doing this stuff in a Xamarin.IOs project.
The NuGet I use for MVVMLight SimpleIoc is this.
I figured out that, to inject a type as a Func, then you have to
Register the type first
Then register it as a Func (consuming the above registered type)
That is,
SimpleIoc.Default.Register<MyInjectingClass>(); // should happen first
SimpleIoc.Default.Register<Func<MyInjectingClass>>(
() => () => SimpleIoc.Default.GetInstance<MyInjectingClass>(
Guid.NewGuid().ToString()
));
The type is simply registered in a way telling it to return a Func object of the MyInjectingClass, that retrieves a new object each time.
Guid.NewGuid().ToString() makes sure a new MyInjectingClass object is returned for the SimpleIoc.Default.GetInstance<MyInjectingClass> method.

How to create a UnityNancyBootstrapper-implementation that will work with a OWIN Startup class

I'm trying to create a Nancy/Unity-bootstrapper that can work with a OWIN-startup-class like the one described here:
Minimal sample: Self hosted Nancy using Owin, Unity bootstrapper including xUnit test
public class Startup
{
public void Configuration(IAppBuilder app)
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IMessageHelper, MessageHelper>();
app.UseNancy(new NancyOptions
{
EnableClientCertificates = true,
Bootstrapper = new NancyOwinBoxBootstrapper(container)
});
}
}
public class NancyOwinBoxBootstrapper : UnityNancyBootstrapper
{
private IUnityContainer _container;
public NancyOwinBoxBootstrapper(IUnityContainer container)
{
_container = container;
}
protected override IUnityContainer GetApplicationContainer()
{
return _container;
}
}
The sample contained in the NancyOwinBox.zip works fine, but it is based on Nancy.Bootstrappers.Unity version 1.1. When i upgrade the NuGet package to the latest version (1.2) the sample fails with:
Resolution of the dependency failed, type = "Nancy.Bootstrapper.IApplicationStartup", name = "Nancy.ViewEngines.ViewEngineApplicationStartup".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The current type, System.Collections.Generic.IEnumerable`1[Nancy.ViewEngines.IViewEngine], is an interface and cannot be constructed. Are you missing a type mapping?
At the time of the exception, the container was:
Resolving Nancy.ViewEngines.ViewEngineApplicationStartup,Nancy.ViewEngines.ViewEngineApplicationStartup (mapped from Nancy.Bootstrapper.IApplicationStartup, Nancy.ViewEngines.ViewEngineApplicationStartup)
Resolving parameter "viewEngines" of constructor Nancy.ViewEngines.ViewEngineApplicationStartup(System.Collections.Generic.IEnumerable1[[Nancy.ViewEngines.IViewEngine, Nancy, Version=1.2.0.0, Culture=neutral, PublicKeyToken=null]] viewEngines, Nancy.ViewEngines.IViewCache viewCache, Nancy.ViewEngines.IViewLocator viewLocator)
Resolving System.Collections.Generic.IEnumerable1[Nancy.ViewEngines.IViewEngine],(none)
The current type, System.Collections.Generic.IEnumerable`1[Nancy.ViewEngines.IViewEngine], is an interface and cannot be constructed. Are you missing a type mapping?
Does anyone have an idea why this error occurs?
You need to register the EnumerableExtension in your container, like so:
container.AddNewExtension<EnumerableExtension>();
See UnityNancyBootstrapper.cs for Reference.

Having issues mapping from a simple list to a corresponding property in a ViewDto

I have a collection of "Alerts" (List) that I need to map to a property in a more complex class.
My destination class hierarchy is as follows:
public class BaseReplyDto<T> where T : class, new()
{
public List<ErrorType> Errors { get; set; }
public T ReplyData { get; set; }
}
public class GetAllAlertsReplyViewDto : BaseReplyDto<List<Alert>>{}
My Mapper configuration is this:
Mapper.CreateMap<List<Alert>, GetAllAlertsReplyViewDto>()
.ForMember(dest => dest.Errors, opt => opt.Ignore())
;
When I run the app, I get a mapper validation error:
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: AutoMapper.AutoMapperConfigurationException:
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
List`1 -> GetAllAlertsReplyViewDto (Destination member list)
System.Collections.Generic.List`1[[bioSynq.Infrastructure.Entities.Concrete.Alert, bioSynq.Infrastructure.Entities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] -> bioSynq.Infrastructure.ViewDtos.DisplayAlertsArea.GetAllAlertsReplyViewDto (Destination member list)
ReplyData
Basically, it's telling me I need to provide some configuration to map my list of Alerts into the list of alerts that is my ReplyData property.
I've mucked around with about twenty different versions of syntax (.ForMember, .ForSourceMember, etc), but can't find the right syntax to get what seems to be a very simple mapping of one list into another.
Anyone know the correct syntax to do this?
In your mapping configuration I don't see configuration for property public List<Alert> ReplyData { get; set; } from GetAllAlertsReplyViewDto class. My guess is that you need something like this
Mapper.CreateMap<List<Alert>, GetAllAlertsReplyViewDto>()
.ForMember(dest => dest.Errors, opt => opt.Ignore())
.ForMember(dest => dest.ReplyData , opt => opt.MapFrom(list => list));
Hope it'll help.

Resources