zoukankan      html  css  js  c++  java
  • Revit:二开使用Sqlite保存本地数据,并配合EF6等ORM框架

    前人种树,后人乘凉。

    在Revit中如何使用Sqlite是一项非常艰难的事情。

    这也是我写在这里的感言,前后花了满满2个工作日,搜遍全网零碎的代码,最终才拼出解决方案。
    你想要直接的解决方案,绝对没有,一定是自己摸索解决的。如果你哪天看到有了,那么你看到的文章极有可能是我写的,或者别人转载了我的文章。

    使用原生的System.Data.Common调用SQlite是一件轻松简单的事情:

    using (var conn = new SQLiteConnection(@"data source=E:公司项目RevitDevelopmentinDebugRevit.db"))
    {
    using (var cmd = conn.CreateCommand())
    {
    conn.Open();
    cmd.CommandText = "select * from users";
    var reader = cmd.ExecuteReader();
    while (reader.Read())
    {
    var id = reader["id"];
    var wxid = reader["wxid"];
    var city = reader["City"];
    }
    }
    }
    

      

    代码如上,这是原生的写法,这种写法,不需要研究,便可以执行,在网上稍微找一下就好。百度就不用解释了,在IT领域,想解决稍微难一点的问题,基本没有百度什么事。你懂的。
    我在研究过程中,几度想要放弃,因为原生写法是可以实现并使用Sqlite的,所以并不是非得找到结合EF6等ORM框架的实现方式不可。我之所以不断研究,是因为我想要使用EF6的ORM框架,而不是原生的方式。

    使用EF6,我就可以获得类似下面的写法,就不需要依靠原始的手段转换来转换去:

    /// <summary>
        /// 仓储基类
        /// </summary>
        /// <typeparam name="TEntity">Type of the Entity for this repository</typeparam>
        /// <typeparam name="TPrimaryKey">Primary key of the entity</typeparam>
        public abstract class RepositoryBase<TDbContext, TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey>
            where TEntity : class, IEntity<TPrimaryKey>, new()
            where TDbContext : DbContext
        {
            /// <summary>
            /// 数据上下文
            /// </summary>
            protected virtual TDbContext Context { get; }
    
            /// <summary>
            /// 主键id
            /// </summary>
            public TPrimaryKey Id { get; set; }
    
            /// <summary>
            /// 获取给定类型的表
            /// </summary>
            public virtual DbSet<TEntity> Table => Context.Set<TEntity>();
    
            /// <summary>
            /// 附加实体,若不存在的话
            /// </summary>
            /// <param name="entity"></param>
            protected virtual void AttachIfNot(TEntity entity)
            {
                if (!Table.Local.Contains(entity))
                {
                    Table.Attach(entity);
                }
            }
    
            /// <summary>
            /// 判断是否添加或更新
            /// </summary>
            /// <returns></returns>
            protected virtual bool IsTransient()
            {
                bool isTransient;
                if (EqualityComparer<TPrimaryKey>.Default.Equals(Id, default(TPrimaryKey)))
                {
                    isTransient = true;
                }
                else
                {
                    isTransient = false;
                }
    
                if (typeof(TPrimaryKey) == typeof(int))
                {
                    isTransient = Convert.ToInt32(Id) <= 0;
                }
    
                if (typeof(TPrimaryKey) == typeof(long))
                {
                    isTransient = Convert.ToInt64(Id) <= 0;
                }
    
                return isTransient;
            }
    
            /// <summary>
            /// 取全部
            /// </summary>
            /// <returns></returns>
            public virtual IQueryable<TEntity> GetAll()
            {
                Context.Database.Log = Console.Write;
                return Table;
            }
    
            /// <summary>
            /// 取所有
            /// </summary>
            /// <returns></returns>
            public virtual IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors)
            {
                if (propertySelectors == null || propertySelectors.Count() <= 0)
                {
                    return GetAll();
                }
    
                var query = GetAll();
    
                foreach (var propertySelector in propertySelectors)
                {
                    query = query.Include(propertySelector);
                }
    
                return query;
            }
    
            /// <summary>
            /// 添加
            /// </summary>
            /// <remarks>
            /// Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded.
            /// 处理办法,没实践过
            /// https://stackoverflow.com/questions/6819813/solution-for-store-update-insert-or-delete-statement-affected-an-unexpected-n
            /// </remarks>
            /// <returns></returns>
            public virtual TEntity Insert(TEntity entity)
            {
                Table.Add(entity);
    
                return entity;
            }
    
            /// <summary>
            /// 批量添加
            /// </summary>
            /// <param name="entities"></param>
            /// <returns></returns>
            public List<TEntity> InsertRange(List<TEntity> entities)
            {
                Context.Set<TEntity>().AddRange(entities);
                return entities;
            }
    
            /// <summary>
            /// 修改
            /// </summary>
            /// <returns></returns>
            public virtual void Update(TEntity entity)
            {
                AttachIfNot(entity);
                Context.Entry(entity).State = EntityState.Modified;
            }
    
            /// <summary>
            /// 修改
            /// </summary>
            /// <param name="entities"></param>
            public void UpdateRange(params TEntity[] entities)
            {
                foreach (TEntity t in entities)
                {
                    AttachIfNot(t);
                    Context.Entry(t).State = EntityState.Modified;
                }
            }
    
            /// <summary>
            /// 添加或修改
            /// </summary>
            public virtual void InsertOrUpdate(TEntity entity)
            {
                this.Id = entity.Id;
                if (this.IsTransient())
                {
                    this.Insert(entity);
                }
                else
                {
                    this.Update(entity);
                }
            }
    
            /// <summary>
            /// 删除
            /// </summary>
            /// <returns></returns>
            public virtual void Delete(TEntity entity)
            {
                AttachIfNot(entity);
                Context.Set<TEntity>().Remove(entity);
            }
    }
    }
    

      

    我从错误的调试中,发现这个问题的原因在于IExternalCommand,是Revit直接调用,他不会加载app.config中的配置,所以什么东西都没有。
    这就意味着,要在IExternalCommand中,动态加载app.config。
    我很高兴去一试,发现根本不行。尝试了各种的动态ConfigurationManager.OpenMappedExeConfiguration()方法,发现他并不是真正的加载到运行时,只是让你可以读取该config文件中的配置信息,仅此而已。

    但,我们想要的并非是读取app.config中的配置信息,而是运行时加载它,让他生效。不是吗?

    于是,通过一翻查找,没有找到直接答案,但却有一个关键方法让我引起了注意,就是 DbProviderFactories.GetFactory("System.Data.SQLite");但在Revit调用时,始终报错:未加载或注册指定的DbProvider程序。我就日了狗了,卡在这里好久,弄不明白要怎么办。

    通过上述的DbProviderFactories,想到要动态加载Sqlite数据库工厂实例,方向是对的。
    最终通过下述代码得到了DbProvider的实例:

    DbProviderFactory factory = System.Data.SQLite.SQLiteFactory.Instance;
    

    实例获得后,我满满高兴,以为可以开始使用SQlite了,结果,报错,没有EntityFramwork节点的相关注册配置信息。

    所以问题转换为怎么动态生成EntityFramework节点信息。EntityFramework节点内容大致如下:
    <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlCeConnectionFactory, EntityFramework">
    <parameters>
    <parameter value="System.Data.SqlServerCe.4.0" />
    </parameters>
    </defaultConnectionFactory>
    <providers>
    <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    <provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
    <provider invariantName="System.Data.SQLite" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
    </providers>
    </entityFramework>
    我找来找去,都没有找到如何操控这个EntityFramework节点内容的相关代码。因为我看到他是微软内部类internal的。不是public类。所以,理论上,外部根本无法访问此类。
    于是我转为寻找如何操控该内部类的方法,终于被我找到一篇文章。
    一开始我没有觉得这样子就可以了,我已经被使用Sqlite这个问题折腾了2天。基本不带什么希望。
    结果,在我最终将要放弃的时候,按文章的代码一试,靠,竟然...竟然...竟然...成功了!!

                this.SetProviderServices("System.Data.SqlClient", System.Data.Entity.SqlServer.SqlProviderServices.Instance);
                this.SetProviderServices("System.Data.SQLite.EF6", service);
                this.SetProviderServices("System.Data.SQLite", service);
    

      

    通过上述的代码,可以注册EntityFramework节点信息,等同于在运行时加载了app.config中的entityFramework段。

    于是,在Revit二开范围内,无法加载app.config的情况下,针对如何使用SQlite结合EF6的ORM框架来使用Sqlite数据库的功能就实现了。

  • 相关阅读:
    ssh-keygen的使用方法(无密码访问)
    ubuntu solute two different terminals cmd
    ubuntu 查看系统是32位还是64位
    pyplot 绘图与可视化
    python 正则表达式的处理
    python&pandas 与mysql 连接
    Ubuntu 11.10 H3C iNode 客户端安装
    Vijos1055(极大子矩阵)
    Vijos1055(极大子矩阵)
    luoguP2701 [USACO5.3]巨大的牛棚Big Barn(极大子矩阵)
  • 原文地址:https://www.cnblogs.com/mazhiyuan/p/13563048.html
Copyright © 2011-2022 走看看