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数据库的功能就实现了。

  • 相关阅读:
    城市的划入划出效果
    文本溢出省略解决笔记css
    长串英文数字强制折行解决办法css
    Poj 2352 Star
    树状数组(Binary Indexed Trees,二分索引树)
    二叉树的层次遍历
    Uva 107 The Cat in the Hat
    Uva 10336 Rank the Languages
    Uva 536 Tree Recovery
    Uva10701 Pre, in and post
  • 原文地址:https://www.cnblogs.com/mazhiyuan/p/13563048.html
Copyright © 2011-2022 走看看