orm框架(持久化流程)

session是事务 (transaction) 的工厂,处理session后,所有更改将自动刷新到数据库中。或者,如果要处理何时将更改刷新到数据库,即transaction将在session处理完后异步提交。session也可以取消事务

新建两个项目,data 与 data.abstractions, 其中 data.abstractions 为对外抽象接口(面向对象设计原则),并用nuget添加程序包。

 

 

 

public interface ifeatureinfo
    {
        string id { get; }
        string name { get; }
        int priority { get; }
        string category { get; }
        string description { get; }
        bool defaulttenantonly { get; }
        //iextensioninfo extension { get; }
        string[] dependencies { get; }
    }

  

public interface ifeaturemanager
    {
        ienumerable<ifeatureinfo> getfeatures();
        ienumerable<ifeatureinfo> getfeatures(string[] featureidstoload);
        ienumerable<ifeatureinfo> getfeaturedependencies(string featureid);
        ienumerable<ifeatureinfo> getdependentfeatures(string featureid);
        ifeatureinfo getfeaturefordependency(type dependency);
        void tryadd(type type, ifeatureinfo feature);
    }

  

public class databaseprovider
    {
        public string name { get; set; }
        public string value { get; set; }
        public bool hasconnectionstring { get; set; }
        public bool hastableprefix { get; set; }
        publi

  

/// <summary>
    /// 数据库迁移管理
    /// </summary>
    public interface idatamigrationmanager
    {
        /// <summary>
        ///返回具有至少一个数据迁移类的特性,并调用相应的升级方法
        /// </summary>
        task<ienumerable<string>> getfeaturesthatneedupdateasync();

        /// <summary>
        /// 运行所有需要更新的迁移。
        /// </summary>
        task updateallfeaturesasync();

        /// <summary>
        /// 将数据库更新为指定功能的最新版本
        /// </summary>
        task updateasync(string feature);

        /// <summary>
        /// 将数据库更新为指定功能的最新版本
        /// </summary>
        task updateasync(ienumerable<string> features);

        /// <summary>
        /// 执行脚本删除与该特性相关的任何信息
        /// </summary>
        /// <param name="feature"></param>
        task uninstall(string feature);
    }

  

public interface idbconnectionaccessor
    {
        /// <summary>
        /// 创建数据库连接
        /// </summary>
        /// <returns></returns>
        dbconnection createconnection();
    }

  

/// <summary>
    /// 数据库迁移工具,封装yessql功能,直接修改数据库结构
    /// </summary>
    public interface ischemabuilder
    {
        yessql.sql.ischemabuilder schemabuilder { get; set; }
    }

  

 

 

public static class dataaccess
    {
        public static iapplicationbuilder usedataaccess(this iapplicationbuilder app)
        {
            return app.usemiddleware<commitsessionmiddleware>();
        }


        /// <summary>
        /// 添加数据库
        /// </summary>
        /// <param name="services"></param>
        /// <param name="databasetype">数据库类型,支持:sqlconnection,sqlite,mysql,postgres</param>
        /// <param name="connectionstring">sqlite为yessql.db文件所在路径,其他数据库为连接字符串</param>
        /// <param name="tableprefix">表名前缀</param>
        /// <returns></returns>
        public static iservicecollection adddataaccess(this iservicecollection services, string databasetype, string connectionstring, string tableprefix = null)
        {
            services.addscoped<idatamigrationmanager, datamigrationmanager>();

            // adding supported databases
            services.tryadddataprovider(name: "sql server", value: "sqlconnection", hasconnectionstring: true, hastableprefix: true, isdefault: false);
            services.tryadddataprovider(name: "sqlite", value: "sqlite", hasconnectionstring: false, hastableprefix: false, isdefault: true);
            services.tryadddataprovider(name: "mysql", value: "mysql", hasconnectionstring: true, hastableprefix: true, isdefault: false);
            services.tryadddataprovider(name: "postgres", value: "postgres", hasconnectionstring: true, hastableprefix: true, isdefault: false);

            // configuring data access
            services.addsingleton<istore>(sp =>
            {
                iconfiguration storeconfiguration = new yessql.configuration();

                switch (databasetype)
                {
                    case "sqlconnection":
                        storeconfiguration
                            .usesqlserver(connectionstring, isolationlevel.readuncommitted)
                            .useblockidgenerator();
                        break;
                    case "sqlite":
                        var databasefolder = connectionstring;
                        var databasefile = path.combine(databasefolder, "yessql.db");
                        directory.createdirectory(databasefolder);
                        storeconfiguration
                            .usesqlite($"data source={databasefile};cache=shared", isolationlevel.readuncommitted)
                            .usedefaultidgenerator();
                        break;
                    case "mysql":
                        storeconfiguration
                            .usemysql(connectionstring, isolationlevel.readuncommitted)
                            .useblockidgenerator();
                        break;
                    case "postgres":
                        storeconfiguration
                            .usepostgresql(connectionstring, isolationlevel.readuncommitted)
                            .useblockidgenerator();
                        break;
                    default:
                        throw new argumentexception("unknown database type: " + databasetype);
                }

                if (!string.isnullorwhitespace(tableprefix))
                {
                    storeconfiguration = storeconfiguration.settableprefix(tableprefix + "_");
                }

                var store = storefactory.createasync(storeconfiguration).getawaiter().getresult();
                var indexes = sp.getservices<iindexprovider>();

                store.registerindexes(indexes);

                return store;
            });

            services.addscoped(sp =>
            {
                var store = sp.getservice<istore>();

                if (store == null)
                {
                    return null;
                }

                var session = store.createsession();

                var scopedservices = sp.getservices<iindexprovider>();

                session.registerindexes(scopedservices.toarray());

                var httpcontext = sp.getrequiredservice<ihttpcontextaccessor>()?.httpcontext;

                if (httpcontext != null)
                {
                    httpcontext.items[typeof(yessql.isession)] = session;
                }

                return session;
            });

            services.addtransient<idbconnectionaccessor, dbconnectionaccessor>();

            return services;
        }
    }

    public class commitsessionmiddleware
    {
        private readonly requestdelegate _next;

        public commitsessionmiddleware(requestdelegate next)
        {
            _next = next;
        }

        public async task invoke(httpcontext httpcontext)
        {
            await _next.invoke(httpcontext);

            // don't resolve to prevent instantiating one in case of static sites
            var session = httpcontext.items[typeof(yessql.isession)] as yessql.isession;

            if (session != null)
            {
                await session.commitasync();
            }
        }
    }

  

 

public static class dataprovider
    {
        public static iservicecollection tryadddataprovider(this iservicecollection services, string name, string value, bool hasconnectionstring, bool hastableprefix, bool isdefault)
        {
            for (var i = services.count - 1; i >= 0; i--)
            {
                var entry = services[i];
                if (entry.implementationinstance != null)
                {
                    var databaseprovider = entry.implementationinstance as databaseprovider;
                    if (databaseprovider != null && string.equals(databaseprovider.name, name, stringcomparison.ordinalignorecase))
                    {
                        services.removeat(i);
                    }
                }
            }

            services.addsingleton(new databaseprovider { name = name, value = value, hasconnectionstring = hasconnectionstring, hastableprefix = hastableprefix, isdefault = isdefault });

            return services;
        }
    }