简体   繁体   中英

Injecting database configuration values that might change per request in Simple Injector using ResolveUnregisteredType event

I have a SettingRegistration method like below.

static void SettingRegistration(this ContainerOptions options)
{
    options.Container.ResolveUnregisteredType += (sender, e) =>
    {
        //The type implemented ISetting interface
        if (e.UnregisteredServiceType.GetInterface("ISetting")!=null)
        {
            var settingInstance = (ISetting)Activator.CreateInstance(e.UnregisteredServiceType);
            var settingService = ((Container) sender).GetInstance<ISettingService>();
            var s = settingService.GetSetting(settingInstance);

            // Register the instance as singleton.
            e.Register(() => s);
        }
    };
}

The methot that register class with Singleton Lifestyle.

My question is: How to register with Scoped Lifestyle in this context.

UPDATE

I have got application setting classes.

public class StoreSetting:ISetting
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class CartSetting:ISetting
{
    public string CartType { get; set; }
}

As you can see, ISetting interface is represented these setting classes.

public interface ISetting
{        
}

On the ResolveUnregisteredType event, I need to populate them from database on every request. Same application was made at nopCommerce setting infrastructure. I am confused. Do you give me a code snippet?

There is a UnregisteredTypeEventArgs.Register overload that accepts a Registration instance. You can create it using Lifestyle.Scoped.CreateRegistration(Type, Func<object>, Container) .

From your update, it becomes more clear what you are trying to achieve. You are loading configuration settings from the database per request. Your configuration settings are in your case runtime data (since it changes per request). As described here however, Injecting runtime data into components is an anti-pattern.

So instead of using the ResolveUnregisteredType event to hook up these values, my advise is to change your design so that runtime configuration values are loaded after the object graph has been built. You can do this by defining an interface for this:

public interface ISettingProvider<TSetting> where TSetting : ISetting
{
    TSetting Value { get; }
}

The use of this provider abstraction allows to delay the loading of these configuration values after the object graph has been built. A generic implementation can be defined as follows:

public class SettingProvider<TSetting> : ISettingProvider<TSetting>
    where TSetting : ISetting, new()
{
    private readonly ISettingService service;

    public SettingProvider(ISettingService service) {
        this.service = service;
    }

    public TSetting Value => this.service.GetSetting(new TSetting());
}

This generic class can be registered as follows:

container.Register(typeof(ISettingProvider<>), typeof(SettingProvider<>));

This way you can inject the proper ISettingProvider<TSetting> into a consumer:

public class ProcessCartHandler
{
    private readonly ISettingProvider<CartSetting> cartSettingProvider;

    public ProcessCartHandler(ISettingProvider<CartSetting> cartSettingProvider) {
        this.cartSettingProvider = cartSettingProvider;
    }

    public void Handle(ProcessCartHandler command) {
        // Read configuration value
        string cartType = cartSettingProvider.Value.CartType;
        // Use it.
    }
}

Here is the solution I used:

Container config:

private static void InitializeContainer(Container container)
{
    container.Register<IDataContextAsync, AppContext>(Lifestyle.Scoped);
    container.Register<IUnitOfWorkAsync, UnitOfWork>(Lifestyle.Scoped);
    container.Register<IRepositoryAsync<Setting>, Repository<Setting>>(Lifestyle.Scoped);
    container.Register<ISettingService, SettingService>(Lifestyle.Scoped);

    container.Register(typeof(ISettingProvider<>), typeof(SettingProvider<>));
    //container.Options.SettingRegistration();
}

SettingProvider

public interface ISettingProvider<TSetting> where TSetting : ISetting
{
    TSetting Value { get; }
}


public class SettingProvider<TSetting> : ISettingProvider<TSetting> 
    where TSetting : ISetting, new()
{
    private readonly ISettingService service;
    public SettingProvider(ISettingService service)
    {
        this.service = service;
    }

    public TSetting Value => this.service.LoadSetting<TSetting>();

}

SettingServices

public interface ISettingService : IService<Setting>
{
    T LoadSetting<T>() where T : ISetting, new();
    ISetting LoadSettingOLD(object setting);
    void SetSetting(ISetting setting);
}

public class SettingService : Service<Setting>, ISettingService {
    private readonly IRepositoryAsync<Setting> _repository;

    public SettingService(IRepositoryAsync<Setting> repository) : base(repository) {
        _repository = repository;
    }

    public T LoadSetting<T>() where T : ISetting, new() {
        var setting = Activator.CreateInstance<T>();

        var settingName = setting.GetType().Name;
        var properties = setting.GetType().GetProperties();
        var ayars = GetAllSettingsCached()
            .Where(x => x.Name.StartsWith(settingName)).ToList();

        foreach (var propertyInfo in properties)
        {
            var settingPropertyName = settingName + "." + propertyInfo.Name;
            var q = ayars.FirstOrDefault(x => x.Name == settingPropertyName);
            if (q != null)
            {
                propertyInfo.SetValue(setting, 
                    Convert.ChangeType(q.Value, propertyInfo.PropertyType), null);
            }
        }
        return setting;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM