具有多个插入请求的 NHibernate 性能不佳

NHibernate bad performance with multiple insert request

我使用 nhibernate 的 .net 5 网站遇到性能问题。

我有控制器和应用程序使用的操作方法,也被转为 api 调用

action 简单地插入一条记录到这种类型

public class Ente : BaseObject<Ente, string>
{
    public override string Id { get; set; }
    public virtual string Descrizione { get; set; }
    public virtual bool Perimetro { get; set; }
    public virtual int Ordine { get; set; }
}

其中 baseObject 实现了 Equals 方法

当应用程序收到多个 rest 调用时会出现性能问题:每次插入大约需要 2 秒

我可以连续接听数百个电话,系统只需几分钟即可管理所有电话

这是存储库中的保存方法:

protected ISession _session;
    public CrudlRepository(ISession session)
    {
        _session = session;
    }
    public virtual void Create(TEntity entity)
    {
        _session.Save(entity);
    }

这是动作方法

 [HttpPost]
    public IActionResult Insert(string values)
    {
        var obj = new Ente ();
        using (myUow = myUowFactory.Create())
        {
            JsonConvert.PopulateObject(values, obj);
            myUow.Repository.Create(obj);
            myUow.SaveAll();
        }
        return Ok("Operazione completata con successo.");
    }

这是我的配置:

string connectionString = DbSettings.Current.ConnectionStrings.OracleWHB;

        services.AddSingleton<ISessionFactory>(sp => Fluently.Configure()
            .Database(OracleManagedDataClientConfiguration.Oracle10
                .ConnectionString(DbSettings.Current.ConnectionStrings.OracleWHB)
                .DoNot.ShowSql()
                .DoNot.FormatSql())
            .Mappings(m =>
                m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())
                    .Conventions.AddFromAssemblyOf<UnitOfWork>()
            )
            .ExposeConfiguration(config => SetupEnvers(config, sp))
            .ExposeConfiguration(config => new SchemaUpdate(config).Execute(false, genereteDB))

            //https://weblogs.asp.net/ricardoperes/on-nhibernate-performance per dettagli
            .ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.GenerateStatistics, Boolean.FalseString))
            .ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.Hbm2ddlKeyWords, Hbm2DDLKeyWords.None.ToString()))
            .ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.PrepareSql, Boolean.TrueString))
            .ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.PropertyBytecodeProvider, "lcg"))
            .ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.PropertyUseReflectionOptimizer, Boolean.TrueString))
            .ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.QueryStartupChecking, Boolean.FalseString))
            .ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.UseProxyValidator, Boolean.FalseString))
            .ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.UseSecondLevelCache, Boolean.FalseString))
            .ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.UseSqlComments, Boolean.FalseString))
            .ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.UseQueryCache, Boolean.FalseString))
            .ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.WrapResultSets, Boolean.TrueString))

            .BuildSessionFactory()
        );
        services.AddScoped<ISession>(sp => sp.GetService<ISessionFactory>().OpenSession());
        services.AddTransient<IRevisionListener, EnversRevisionListener>();

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

        return services;

基础对象

public abstract class BaseObject<TEntity, TKey> : EquatableObject<TEntity>
    where TEntity : class
    {
        public abstract TKey Id { get; set; }

        public override int GetHashCode()
        {
            return Id.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            BaseObject<TEntity, TKey> other = obj as BaseObject<TEntity, TKey>;

            return BaseObjectEquals(other);

        }

        public override bool Equals(TEntity obj)
        {
            if (obj == null)
                return false;

            BaseObject<TEntity, TKey> other = obj as BaseObject<TEntity, TKey>;

            return BaseObjectEquals(other);

        }

        public virtual bool BaseObjectEquals(BaseObject<TEntity, TKey> other)
        {
            if (other == null)
                return false;

            return EqualityComparer<TKey>.Default.Equals(this.Id , other.Id);
        }

        private IEnumerable<FieldInfo> GetFields()
        {
            Type t = GetType();

            List<FieldInfo> fields = new List<FieldInfo>();
            while (t != typeof(object))
            {
                fields.AddRange(t.GetTypeInfo().DeclaredFields.Where(x => x.FieldType.Name != typeof(ICollection<>).Name));//.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public));

                t = t.GetTypeInfo().BaseType;
            }
            return fields;
        }

        public static bool operator ==(BaseObject<TEntity, TKey> x, BaseObject<TEntity, TKey> y)
        {
            // If both are null, or both are same instance, return true.
            if (System.Object.ReferenceEquals(x, y))
            {
                return true;
            }

            // If one is null, but not both, return false.
            if (((object)x == null) || ((object)y == null))
            {
                return false;
            }

            return x.Equals(y);
        }

        public static bool operator !=(BaseObject<TEntity, TKey> x, BaseObject<TEntity, TKey> y)
        {
            return !(x == y);
        }
    }

EquatableObject

public abstract class EquatableObject<TObject>: IEquatable<TObject>
    where TObject : class

{
    ///  < summary >
    /// Default Constructor.
    ///  < /summary >
    protected EquatableObject()
    {
    }

    public override int GetHashCode()
    {
        Type t = GetType();

        TypeInfo typeInfo = t.GetTypeInfo();
        IEnumerable<FieldInfo> fields = typeInfo.DeclaredFields.Where(x => x.FieldType.Name != typeof(ICollection<>).Name);

        int startValue = 17;
        int multiplier = 59;
        int hashCode = startValue;

        foreach (FieldInfo field in fields)
        {
            object value = field.GetValue(this);
            if (value != null)
                hashCode = hashCode * multiplier + value.GetHashCode();
        }
        return hashCode;
    }

    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;

        TObject other = obj as TObject;

        return Equals(other);

    }

    public virtual bool Equals(TObject other)
    {
        if (other == null)
            return false;

        Type t = GetType();

        TypeInfo typeInfo = t.GetTypeInfo();
        IEnumerable<FieldInfo> fields = typeInfo.DeclaredFields.Where(x => x.FieldType.Name != typeof(ICollection<>).Name);
        foreach (FieldInfo field in fields)
        {
            object value1 = field.GetValue(other);
            object value2 = field.GetValue(this);

            if (value1 == null)
            {
                if (value2 != null)
                    return false;
            }
            else if (!value1.Equals(value2))
                return false;
        }
        return true;

    }


    public static bool operator ==(EquatableObject<TObject> x, EquatableObject<TObject> y)
    {
        // If both are null, or both are same instance, return true.
        if (System.Object.ReferenceEquals(x, y))
        {
            return true;
        }

        // If one is null, but not both, return false.
        if (((object)x == null) || ((object)y == null))
        {
            return false;
        }

        return x.Equals(y);
    }

    public static bool operator !=(EquatableObject<TObject> x, EquatableObject<TObject> y)
    {
        return !(x == y);
    }
}

存储库中全部保存

public void SaveAll()
    {
        if (_transaction != null)
        {
            _transaction.Commit();
            _transaction = null;
        }
    }

您应该检查问题是否不在数据库级别,性能调整只能在测量所有涉及的部分后才能进行,但基于您显示的代码:

如果您使用的 nhibernate 版本支持异步调用,您可以使用类似

await _transaction.CommitAsync();

然后,在任何情况下,您都可以在像您描述的长 I/O 调用的并发场景中从中受益。如果数据库服务器运行正常,我猜这是您的主要问题。

async in depth on microsoft.com

Throughout this entire process, a key takeaway is that no thread is dedicated to running the task. Although work is executed in some context (that is, the OS does have to pass data to a device driver and respond to an interrupt), there is no thread dedicated to waiting for data from the request to come back. This allows the system to handle a much larger volume of work rather than waiting for some I/O call to finish.

为避免被迫将所有方法更改为异步,请添加异步版本的 SaveAll(),例如SaveAllAsync(),这将需要更改为仅异步此控制器中的方法。整体收益将小于完全改变,但您至少可以测试一下它是否是一条好路。

另一方面,在您的 equatable 对象中,您可以将反射的枚举缓存在某处,这样您就不需要每次都执行此操作:

    Type t = GetType();
    TypeInfo typeInfo = t.GetTypeInfo();
    IEnumerable<FieldInfo> fields = typeInfo.DeclaredFields.Where(x => x.FieldType.Name != typeof(ICollection<>).Name);