Injecting an instance of a service with Autofac - c#

I have a problem with Autofac injection or registration.
This is my code
Repository
namespace ClientConfiguration.Data.Repository
{
public class MappingBaseRepository : RepositoryBase<MappingBase>, IMappingBaseRepository
{
public MappingBaseRepository(IDatabaseFactory databaseFactory)
: base(databaseFactory)
{
}
}
public interface IMappingBaseRepository : IRepository<MappingBase>
{
}
}
Service
namespace ClientConfiguration.Service {
public interface IMappingBaseService
{
IEnumerable<MappingBase> GetElements();
void SaveElement();
}
public class MappingBaseService : IMappingBaseService
{
private readonly IMappingBaseRepository MappingBaseRepository;
private readonly IUnitOfWork unitOfWork;
public MappingBaseService(IMappingBaseRepository MappingBaseRepository, IUnitOfWork unitOfWork)
{
this.MappingBaseRepository = MappingBaseRepository;
this.unitOfWork = unitOfWork;
}
#region Members
public IEnumerable<MappingBase> GetElements()
{
var Elements = MappingBaseRepository.GetAll();
return Elements;
}
public void SaveElement()
{
unitOfWork.Commit();
}
#endregion
} }
Autofac init
private static void SetAutofacContainer() {
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
builder.RegisterType<DatabaseFactory>().As<IDatabaseFactory>().InstancePerRequest();
// Repositories
builder.RegisterAssemblyTypes(typeof(ClientElementRepository).Assembly)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces().InstancePerRequest();
// Services
builder.RegisterAssemblyTypes(typeof(ClientElementService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().InstancePerRequest();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
Now if I'm inside a controller I have an instance of the service objects without problem. But I have to access my service IMappingBaseService to get data from DB inside this class:
namespace ClientConfiguration.Mappings {
public class CustomDisplayNameAttribute : DisplayNameAttribute {
private static IMappingBaseService mappingBaseService { get; set; }
public CustomDisplayNameAttribute(string value)
: base(GetMessageFromResource(value)) {
}
private static string GetMessageFromResource(string value) {
var els = mappingBaseService.GetElements().ToList();
//get value from DB
//mappingBaseService is always null
return "";
}
}
}
Any help would be greatly appreciated! Thanks in advance.

Code demo such as:
namespace ClientConfiguration.Mappings {
public class CustomDisplayNameAttribute : DisplayNameAttribute {
private static IMappingBaseService _mappingBaseService { get; set; }
public CustomDisplayNameAttribute(string value, IMappingBaseService mappingBaseService)
: base(GetMessageFromResource(value, mappingBaseService)) {
}
private static string GetMessageFromResource(string value, IMappingBaseService mappingBaseService) {
_mappingBaseService = mappingBaseService;
var els = _mappingBaseService .GetElements().ToList();
//OR var els = mappingBaseService.GetElements().ToList();
//get value from DB
//mappingBaseService is always null
return "";
}
}
}

Maybe you can fix code register autofac, Because autofac only register for interface such as:
builder.RegisterAssemblyTypes(typeof(IClientElementRepository).Assembly)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces().InstancePerRequest();
// Services
builder.RegisterAssemblyTypes(typeof(IClientElementService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().InstancePerRequest();
builder.RegisterAssemblyTypes(typeof(IMappingBaseService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().InstancePerRequest();

The solution was to use Property injection (instanciate the class inside the autofac init)
We have to add this line
builder.Register(c => new CustomDisplayNameAttribute {
_mappingBaseService = c.Resolve<IMappingBaseService>() });
and in CustomDisplayNameAttribute we add empty constructor
public CustomDisplayNameAttribute() {}
and
public IMappingBaseService _mappingBaseService { get; set; }
and for getting the object we use
var _mappingBaseService = DependencyResolver.Current.GetService<IMappingBaseService>();

The problem is that is i surcharge CustomDisplayName from DisplayNameAttribute (ASP.NET MVC)
public class ClientElementsViewModel {
private static IMappingBaseService _mappingBaseService;
public ClientElementsViewModel(IMappingBaseService mappingBaseService) {
_mappingBaseService = mappingBaseService;
}
[Key]
[Display(Name = "Id")]
public long ClientElementId { get; set; }
[CustomDisplayName("", _mappingBaseService)]
public string CompanyCode { get; set; }
//[CustomDisplayName("")]
public string WebAppBaseUrl { get; set; }
//[CustomDisplayName("")]
public string GuestTraveller { get; set; }
}
I have this error
Error 3 An attribute argument must be a constant expression, typeof
expression or array creation expression of an attribute parameter
type D:\CDS_ADMIN\ClientConfiguration.Web\ViewModel\ClientElementsViewModel.cs 22 32 ClientConfiguration.Web

Related

I miss ILazyloader (EF Core )after use Activator.CreateInstance() for create Entity

I use Activator.CreateInstance() like below
public class EntityBase
{
public EntityBase()
{
}
private readonly IServiceProvider serviceProvider;
public EntityBase(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
Type propertyType = GetPropertyType("ORMLawEntity");
Type type = propertyType.GetGenericArguments().Single();
object obj = Activator.CreateInstance(type);
}
to create the below Entity:
public class ORMLawEntity : EntityBase
{
private readonly ILazyLoader _lazyLoader;
[JsonConstructor]
public ORMLawEntity()
{
base.PrimaryKey = "idLaw";
base.EntityName = "Law";
}
public ORMLawEntity(ILazyLoader lazyLoader)
{
base.PrimaryKey = "idLaw";
base.EntityName = "Law";
_lazyLoader = lazyLoader;
}
[Key]
public long idLaw { set; get; }
private ORMCatalogValueEntity _LawTypeCIDEntity;
public virtual ORMCatalogValueEntity LawTypeCIDEntity
{
set { _LawTypeCIDEntity = value; }
get {
return _lazyLoader.Load(this, ref _LawTypeCIDEntity);
}
}
}
but when I use Activator.CreateInstance(type) default constructor executed and I don't have lazyloader. but I want to fill lazyLoader for LawTypeCIDEntity property.
Add the params inside the CreateInstance method. The appropriate constructor will implicitly be resolved.
Activator.CreateInstance(type, someLazyLoader);
If you want to inject it from a DI container
public class EntityBase
{
private readonly ILazyLoader lazyLoader;
public EntityBase()
{
}
public EntityBase(ILazyLoader lazyLoader)
{
this.lazyLoader = lazyLoader;
}
public ORMLawEntity CreateORMLawEntity()
{
return (ORMLawEntity) Activator.CreateInstance(type, lazyLoader);
}
}

Configure all options derived from base option class or interface .NET Core

I have code with variables in appsettings.json file so I register all options in IServiceCollection via configure method:
public static void Configure(IServiceCollection services, IConfiguration configuration, bool useHangfire = true)
{
services
.Configure<AuthSettings>(configuration.GetSection(AuthSettings.SectionName))
.Configure<CacheSettings>(configuration.GetSection(CacheSettings.SectionName))
..... and so on
I would like to create a base(abstract) class or interface for example
public interface ISettings
{
public const string SectionName = "DefaultSettings";
}
public class AuthSettings: ISettings
{
public const string SectionName = "AuthSettings";
public int ConfirmCodeLength { get; set; }
public string AllowedChars { get; set; }
public TimeSpan ConfirmCodeExpiry { get; set; }
}
and configure all settings like this
foreach (var type in
Assembly.GetAssembly(typeof(ISettings)).GetTypes()
.Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(ISettings))))
{
var currentSettings = (ISettings)Activator.CreateInstance(type);
services.ConfigureOptions(currentSettings);
}
I have alredy done the same with registration of hangfire jobs but this case looks a bit different.
Unfortunately this version doesn't work bacause currentSetting should implenetn IConfigureOptions but it doesn't. Also i'm not sure that this code get values from JSON.
Did someone do something like this?
So, if you has many settings you can create base class for all settings, that has method to define section name
public class BaseSettings
{
public virtual string SectionName => this.GetType().Name;
}
and many settings-classes like this
public class AuthSettings:BaseSettings
{
public int ConfirmCodeLength { get; set; }
public string AllowedChars { get; set; }
public TimeSpan ConfirmCodeExpiry { get; set; }
}
Then in an ServiceCollection extention class set method to find all inherited classes and add it to IServiceCollection
public static IServiceCollection AddAllOptions(this IServiceCollection services, IConfiguration configuration)
{
foreach (var type in
Assembly.GetAssembly(typeof(BaseSettings)).GetTypes()
.Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(BaseSettings))))
{
var configurationInstance = (BaseSettings)Activator.CreateInstance(type);
if (configurationInstance != null)
{
var configurationSection = configuration.GetSection(configurationInstance.SectionName);
var configureMethod = typeof(OptionsConfigurationServiceCollectionExtensions).GetMethods()
.Where(x => x.Name == "Configure")
.Single(m => m.GetParameters().Length == 2)
.MakeGenericMethod(type);
configureMethod.Invoke(null, new object[] { services, configurationSection });
}
}
return services;
}
Finally you can use this method in your StartUp class
service.AddAllOptions(configuration);

StructureMap: conditionally use concrete type based on concrete instance property value

I'm struggling to make StructureMap use one of concrete types sharing a common interface. This is further complicated by the fact that all candidate objects are descendants of an intermediate abstract class.
public interface ICustomer
{
string Id { get; }
}
public abstract class CommonCustomer : ICustomer {
public abstract string Id { get; }
}
// Fallback type if none matched
public class BaseCustomer : CommonCustomer
{
public override string Id { get; } = "Base";
}
// Concrete type 1
public class AlphaCustomer : CommonCustomer
{
public override string Id { get; } = "Alpha";
}
// Concrete type 2
public class BravoCustomer : CommonCustomer
{
public override string Id { get; } = "Bravo";
}
What I tried so far:
Scan(x =>
{
x.TheCallingAssembly();
x.AddAllTypesOf<ICustomer>();
});
var key = "Alpha";
For<ICustomer>().Use("",
context => context.GetAllInstances<ICustomer>()
.FirstOrDefault(x => x.Id == key)).Singleton();
For<ICustomer>().UseIfNone<BaseCustomer>().Singleton();
How can I select a concrete type based on it's string property? And how do I scan through types which do not directly implement ICustomer?
Sounds like you want to create a factory for instantiating ICustomer.
public interface ICustomerFactory
{
ICustomer Create(string key);
}
public class CustomerFactory : ICustomerFactory
{
private readonly IContainer _container;
public CustomerFactory(IContainer container)
{
_container = container;
}
public ICustomer Create(string key) => _container.TryGetInstance<ICustomer>(key);
}
And during configuration of your container naming them:
var container = new Container(c =>
{
c.For<ICustomerFactory>().Use<CustomerFactory>();
c.Scan(x =>
{
x.TheCallingAssembly();
x.AddAllTypesOf(typeof(ICustomer))
.NameBy(t => ((ICustomer)Activator.CreateInstance(t, new object[0], new object[0])).Id);
});
});
Usage:
ICustomerFactory factory;
var customer1 = factory.Create("Alpha");
var customer2 = factory.Create("Bravo");
var customer3 = factory.Create("Base");
var customer4 = factory.Create("NotExisting"); // returns null.

How to access class properties through an Interface instance using Unity.WebApi

Is it possible to expose class public properties in different class through IOC. I am creating an instance of Interface but i am not able to access public properties of class. I am using Unity.WebApi for resolving dependencies.
TransactionService Class
public class TransactionService : ITransactionService
{
private readonly IMRepository _mRepository;
private readonly IFService _fGateway;
public TransactionService(IMbaRepository mbaRepository, IFpnService fpnService)
{
_mRepository = mRepository;
_fGateway = fService;
}
private List<Transaction> SearchTransacionsByUser(FUser objFUser)
{
foreach (var item in something)
{
//can't use _fGateway to set properties because Interface
// don't implement them
_fGateway.OID = objFUser.OID.ToString();
_fGateway.Amount = objFUser.Amount;
_fGateway.Search(criteria);
}
}
}
FService class
public class FService : IFpService
{
public string _OID { get; set; }
public decimal _Amount{ get; set; }
public TransactionResponse Search(string criteria)
{
TransactionOperationInput _input;
_input = new TransactionOperationInput()
{
Criteria = _criteria,
OID = _OID,
Amount = _Amount
};
// search transactions
}
}
If you are in control of the services then refactor the interfaces to expose the desired members
public interface IFService {
TransactionResponse Search(TransactionOperationInput input);
}
Make sure the derived implementation has those members
public class FService : IFpService {
public TransactionResponse Search(TransactionOperationInput input) {
// search transactions
}
}
And that the dependent class uses the correct abstraction
public class TransactionService : ITransactionService {
private readonly IMRepository _mRepository;
private readonly IFService fGateway;
public TransactionService(IMbaRepository mbaRepository, IFService fService) {
_mRepository = mRepository;
fGateway = fService;
}
private List<Transaction> SearchTransacionsByUser(FUser objFUser) {
foreach (var item in something) {
TransactionOperationInput input = new TransactionOperationInput() {
Criteria = _criteria,
OID = objFUser.OID.ToString(),
Amount = objFUser.Amount,
};
fGateway.Search(input);
//...
}
//...
}
}
Finally make sure the register the appropriate abstractions and implementations with the IoC/DI container.

Parameterless public constructor

First time using MS Unity. I have a controller with the following constructor:
protected IAdministrationService AdministrationService { get; set; }
public GenerateCacheController(IAdministrationService administrationService)
{
AdministrationService = administrationService;
}
I get the following error when trying to run the project:
Make sure that the controller has a parameterless public constructor.
In my Bootstrpper.cs file I have the following in the RegisterTypes method:
container.RegisterType<GenerateCacheController>();
I still get the error. Am I missing anything else? I'm using ASP.NET MVC 5 and Unity 3.
Here's my Boostrapper.cs file:
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
}
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterInstance(container);
var im = new InjectionMember[0];
container.RegisterType<IAdministrationService, AdministrationService>("AdministrationService", im);
container.RegisterType<ILookupMapper, LookupMapper>("LookupMapper", im);
container.RegisterType<IEmailService, EmailService>("EmailService", im);
container.RegisterType<GenerateCacheController>();
var provider = new UnityServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => provider);
}
}
Abbreviated version of the AdministrationService class:
public class AdministrationService : IAdministrationService
{
protected ILookupMapper LookupMapper { get; set; }
protected IEmailService EmailService { get; set; }
public AdministrationService(ILookupMapper lookupMapper, IEmailService emailService)
{
LookupMapper = lookupMapper;
EmailService = emailService;
}
}
Found the issue.
I commented out the line:
var im = new InjectionMember[0];
container.RegisterType<IAdministrationService, AdministrationService>("AdministrationService", im);
and added:
container.RegisterType<IAdministrationService, AdministrationService>();
And that worked because the previous developers were doing something like this:
private IUnityContainer Container { get; set; }
public AdministrationService()
{
Container = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<IUnityContainer>();
}
instead of
protected ILookupMapper LookupMapper { get; set; }
protected IEmailService EmailService { get; set; }
public AdministrationService(ILookupMapper lookupMapper, IEmailService emailService)
{
LookupMapper = lookupMapper;
EmailService = emailService;
}
I have to go back to their way to not break existing code. I'll get around to refactoring one day.

Categories