-2

I'am using NHibernate and envers in a .net 5 web application, i need to add user id to default revision entity and find no way to use dependency injection to do it.

this is envers configuration

private static void SetupEnvers(NHibernate.Cfg.Configuration cfg)
    {
        var enversConf = new NHibernate.Envers.Configuration.Fluent.FluentConfiguration();
        enversConf.Audit<Persona>();

        IRevisionListener revListner = new EnversRevisionListener(new IUserManagement);
        enversConf.SetRevisionEntity<RevisionEntity>(e => e.Id, e => e.RevisionDate, revListner);
        cfg.SetEnversProperty(ConfigurationKey.AuditTableSuffix, "_LOG");
        cfg.SetEnversProperty(ConfigurationKey.AuditStrategy, typeof(CustomValidityAuditStrategy));

        cfg.IntegrateWithEnvers(enversConf);
    }

Problem is IRevisionListener revListner = new EnversRevisionListener(new IUserManagement); how can i inject the UserManagement?

i found this topics https://nhibernate.jira.com/browse/NHE-17 but how can i use it?

this is my custom revision entity:

public class RevisionEntity : DefaultRevisionEntity
{
    public virtual string IdUtente { get; set; }
    public virtual string? IdUtenteImpersonato { get; set; }
}

public class EnversRevisionListener : IRevisionListener
{
    IUserManagement um = null;
    public EnversRevisionListener(IUserManagement ium) : base()
    { um = ium; }

    public void NewRevision(object revisionEntity)
    {
        var casted = revisionEntity as RevisionEntity;

        if (casted != null)
        {
            casted.IdUtente = um.GetUtenteCorrente().Id;
            casted.IdUtenteImpersonato = um.GetUtenteCorrente().UtenteImpersonato.Id;
        }
    }
}

this is nhibernate config

    public static IServiceCollection AddNHibernate(this IServiceCollection services, bool genereteDB =         false)
    {   
            _sessionFactory = Fluently.Configure()
                   .Database(OracleManagedDataClientConfiguration.Oracle10
                   .ConnectionString(DbSettings.Current.ConnectionStrings.AbilioDB)
                   .DoNot.ShowSql()
                   .DoNot.FormatSql())
                   .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())
                                                .Conventions.AddFromAssemblyOf<UnitOfWork>()
                        )
                       .ExposeConfiguration(config => SetupEnvers(config))
                       .ExposeConfiguration(config => new SchemaUpdate(config).Execute(false, true))
                .BuildSessionFactory();
        
        services.AddSingleton(_sessionFactory);
        services.AddScoped(factory => _sessionFactory.OpenSession());
        services.AddScoped<IUnitOfWorkFactory, UnitOfWorkFactory>();
        services.AddScoped<IUnitOfWork, UnitOfWork>();

        return services;
    }
gt.guybrush
  • 1,320
  • 3
  • 19
  • 48

1 Answers1

3

Move the session factory creation into a delegate factory when registering it with the container. That delegate factory provides access to a service provider that can be used to resolve the desired service

The current setup code is too tightly coupled to implementation details. By using the deferred factory delegate while configuring services will allow for dependencies to be resolved easier.

This

//...

services.AddSingleton(_sessionFactory);
services.AddScoped(factory => _sessionFactory.OpenSession());

//...

needs to be changed to

public static IServiceCollection AddNHibernate(this IServiceCollection services, bool genereteDB = false) {

    services.AddSingleton<ISessionFactory>(sp => Fluently.Configure()
        .Database(OracleManagedDataClientConfiguration.Oracle10
            .ConnectionString(DbSettings.Current.ConnectionStrings.AbilioDB)
            .DoNot.ShowSql()
            .DoNot.FormatSql())
        .Mappings(m => 
            m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())
                .Conventions.AddFromAssemblyOf<UnitOfWork>()
        )
        .ExposeConfiguration(config => SetupEnvers(config, sp)) //<-- NOTE THIS
        .ExposeConfiguration(config => new SchemaUpdate(config).Execute(false, true))
        .BuildSessionFactory()
    );
    services.AddScoped<ISession>(sp => sp.GetService<ISessionFactory>().OpenSession());
    services.AddTransient<IRevisionListener, EnversRevisionListener>();
    services.AddTransient<IUserManagement, UserManagement>();

    services.AddScoped<IUnitOfWorkFactory, UnitOfWorkFactory>();
    services.AddScoped<IUnitOfWork, UnitOfWork>();

    return services;
}

Where SetupEnvers is refactored to resolve the required service

private static void SetupEnvers(NHibernate.Cfg.Configuration cfg, IServiceProvider services) {
    var enversConf = new NHibernate.Envers.Configuration.Fluent.FluentConfiguration();
    enversConf.Audit<Persona>();

    IRevisionListener revListner = services.GetService<IRevisionListener>();
    enversConf.SetRevisionEntity<RevisionEntity>(e => e.Id, e => e.RevisionDate, revListner);
    cfg.SetEnversProperty(ConfigurationKey.AuditTableSuffix, "_LOG");
    cfg.SetEnversProperty(ConfigurationKey.AuditStrategy, typeof(CustomValidityAuditStrategy));

    cfg.IntegrateWithEnvers(enversConf);
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • code comes from here (https://gunnarpeipman.com/aspnet-core-nhibernate/) but your's is cleaner: in other words it's a service locator: not best in other place but here maybe is the only clean way – gt.guybrush Jan 14 '21 at 13:14
  • p.s. obviously i move services.AddTransient(); in startup.cs – gt.guybrush Jan 14 '21 at 13:30