zoukankan      html  css  js  c++  java
  • ServiceStack.OrmLite中的一些"陷阱"(3)

    前文说到如果使用多数据库(不同SQL方言)时要如何开发?其实前文(第二篇)也有“透露”到。就是直接使用库提供的OrmLiteConnectionOrmLiteConnectionFactory(IDbConnectionFactory) 。我们先来看下代理类是怎么实现的:

    public class OrmLiteConnection : IDbConnection, IHasDbConnection, IHasDbTransaction 
    {
        public readonly OrmLiteConnectionFactory Factory;
        public IDbTransaction Transaction { get; set; }
        private IDbConnection dbConnection;
        private bool isOpen;
    
        public OrmLiteConnection(OrmLiteConnectionFactory factory)
        {
            this.Factory = factory;
        }
    
        public IDbConnection DbConnection
        {
            get
            {
                if (dbConnection == null)
                {
                    dbConnection = Factory.ConnectionString.ToDbConnection(Factory.DialectProvider);
                }
                return dbConnection;
            }
        }
    }
     
    
    public interface IDbConnectionFactory
    {
        IDbConnection CreateDbConnection();
        IDbConnection OpenDbConnection();
    }
    
    public class OrmLiteConnectionFactory : IDbConnectionFactory
    {
        public OrmLiteConnectionFactory(): this(null, null, true) {}
        public OrmLiteConnectionFactory(string connectionString) : this(connectionString, null, true) {}
        public OrmLiteConnectionFactory(string connectionString, IOrmLiteDialectProvider dialectProvider)
            : this(connectionString, dialectProvider, true) {}
    
        public OrmLiteConnectionFactory(string connectionString, IOrmLiteDialectProvider dialectProvider, bool setGlobalDialectProvider)
        {
            ConnectionString = connectionString;
            AutoDisposeConnection = connectionString != ":memory:";
            this.DialectProvider = dialectProvider ?? OrmLiteConfig.DialectProvider;
            if (setGlobalDialectProvider && dialectProvider != null)
            {
                OrmLiteConfig.DialectProvider = dialectProvider;
            }
            this.ConnectionFilter = x => x;
        }
        public IOrmLiteDialectProvider DialectProvider { get; set; }
    }    
     

    在OrmLite中OrmLiteConnection 可是一等公民,如果需要使用IDbConnectionFactory.DialectProvider ,而又想要通过代理实现各种功能的话,只能使用OrmLiteConnection 代理它,而它不能代理OrmLiteConnection 。如果需要代理原始连接,则可以使用OrmLiteConnectionFactory.ConnectionFilter

    factory.ConnectionFilter = x =>new ProxyConnection(x);

    因为前文提到了连接池,我就想应该如何使用IDbConnectionFactory来实现连接池(各位暂且不要先吐槽,等下我自行打面)。

    虽然有OrmLiteConnectionFactory.ConnectionFilter的存在,但是且看OrmLiteConnection 是怎么使用代理连接的:

    public IDbConnection DbConnection
    {
        get
        {
            if (dbConnection == null)
            {
                dbConnection = Factory.ConnectionString.ToDbConnection(Factory.DialectProvider); //(2)
            }
            return dbConnection;
        }
    }
    
    
    public void Dispose()
    {
        if (Factory.OnDispose != null) Factory.OnDispose(this);
        if (!Factory.AutoDisposeConnection) return;
        DbConnection.Dispose();
        dbConnection = null;
        isOpen = false;
    }
    
    public void Open()
    {
        if (isOpen) return;
        DbConnection.Open(); //(1)
        if(Factory.ConnectionFilter != null) 
        {
            dbConnection = Factory.ConnectionFilter(dbConnection);
        }
        isOpen = true;
    }

    先看看Open方法,其内部调用DbConnection.Open()(注释1),而初始dbConnection为null,则需要获取新连接(注释2)。以我的理解,我最初以为是通过IDbConnectionFactory.OpenDbConnection()获取新连接的,这样的话做代理就方便多啦!但是注释2中可是直接通过API创建新连接啊,也就是说,就算我可以做到连接的回收,也无法控制“产出”。
    后来就请教了一个比较熟悉.Net的同学(有无用过OrmLite.IDbConnectionFactory,下一步是问他怎样实现连接池),他还没回复,我就恍然大悟的知道了要实现连接池不应该通过修改IDbConnectionFactory,而是通过代理IOrmLiteDialectProvider !这样的话就不需要修改Factory便可以控制连接的回收和产出!

    而最初迷惑我使用OrmLiteConnectionFactory和OrmLiteConnection配合来实现代理的,正正是两者的例子。
    首先,通过OrmLiteConnectionFactory.ConnectionFilter 创建代理。
    其次,通过OrmLiteConnectionFactory.OnDispose 进行回收。
    最后,OrmLiteConnectionFactory.OrmLiteConnection 的实现使我越陷越深,如果OrmLiteConnectionFactory作为工具类,那么OrmLiteConnection 属性是不应该存在的,而是由继承者实现。如果是用于测试,那只能说这样太不专业了,还有其源码注释也说明了:

    /// <summary>
    /// Allow for mocking and unit testing by providing non-disposing 
    /// connection factory with injectable IDbCommand and IDbTransaction proxies
    /// </summary>
    public class OrmLiteConnectionFactory : IDbConnectionFactory
    {
    }

    说实话当时我都看醉了,要使用OrmLiteConnection不能配合IDbConnectionFactory使用,而必须通过其实现类OrmLiteConnectionFactory ,但OrmLiteConnectionFactory 的接口又不写成virtual,在不修改源码的情况下,是无法通过OrmLiteConnection(Factory)来实现连接池的(知道代理IOrmLiteDialectProvider 之前)。


    而当我多么痛的领悟到要代理IOrmLiteDialectProvider 来实现连接池后,高兴地告诉了我请教那位同学。他告诉我:
    ADO.NET!默!认!带!连!接!池!

    我先是五雷轰顶,然后默默细想:印象中带连接池的是SqlConnection,那是SqlServer的东西吧?我用的可是Sqlite和MySql哦!最后我说明早再找资料看看(也就是今早)

    (我没研究过ADO.NET什么的,我就是个偏科的程序员....)

    后来我查了ADO的相关资料,说ADO.NET规范是默认支持连接池的,而且是默认开启。也就是说,无论是SqlServer还是Sqlite,想要实现ADO.NET那就得遵守它的规范咯!但是作为一看不看源码不安心行人,还是默默地拿起Reflector反编译了Sqlite.DLL:

    internal static class SqliteConnectionPool
    {
        // Fields
        private static SortedList<string, Pool> _connections;
        private static int _poolVersion;
       //...
    }

    果真是有。

    <完>

     

    再提一下,OrmLite不想Hibernate那样可以在对象里存对象然后直接存储ID这么智能,最多只能作为DTO的语言实现,否则OrmLite会直接将整个对象序列化到其中一个字段当中,还是看代码吧:

        public class Hello
        {
            [PrimaryKey]
            [AutoIncrement]
            public int Id { get; set; }
            public string Name { get; set; }
            public World World { get; set; } //改为int WordId吧。
            public Hello(string name, World w)
            {
                Name = name;
                World = w;
            }
        }
        public class World
        {
            [PrimaryKey]
            [AutoIncrement]
            public int Id { get; set; }
            public string Address { get; set; }
            public World(string addr)
            { Address = addr; }
        }

    总结:说到底这其实不算坑,这就是我瞎折腾。告诫各位还是先学好基础把文档看细为妙。

  • 相关阅读:
    工程师的十层楼,上
    工程师的十层楼 (下)
    2011CCTV中国经济年度人物评选结果揭晓
    IT行业程序员薪水差距之大的原因是什么
    单片机C应用开发班
    【分享】对输入子系统分析总结
    P6156 简单题 题解
    P3911 最小公倍数之和 题解
    dp 做题记录
    UVA12298 Super Poker II 题解
  • 原文地址:https://www.cnblogs.com/godzza/p/3997350.html
Copyright © 2011-2022 走看看