惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
WordPress大学
WordPress大学
博客园 - 【当耐特】
The Cloudflare Blog
B
Blog
Last Week in AI
Last Week in AI
小众软件
小众软件
量子位
S
SegmentFault 最新的问题
V
Visual Studio Blog
博客园 - 叶小钗
美团技术团队
阮一峰的网络日志
阮一峰的网络日志
Hugging Face - Blog
Hugging Face - Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
宝玉的分享
宝玉的分享
A
About on SuperTechFans
雷峰网
雷峰网
J
Java Code Geeks
Microsoft Azure Blog
Microsoft Azure Blog
腾讯CDC
MongoDB | Blog
MongoDB | Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Martin Fowler
Martin Fowler

博客园 - 愚

Revity:查找并修改类型参数和实例参数 复习一下UML Revit:ElementFilter过滤器基类 Revit:Element、Reference、ReferenceIntersector、ReferenceWithContext的理解 Revit:使用代码如何发出选点、选对象指令 Reivt:过滤器FilteredElementIterator,采集器FilteredElementCollector Revit:Transaction事务 Revit:弄清楚Application、UIApplication、UIDocument 、Document、DocumentSet之间的关系 Revit:IExternalCommand和IExternalApplication的区别 什么是假程序员 若我有一天 聊聊面试官那些事。。分享我的个人看法,认同的顶一顶。。。 提供一个中国身份证号码判断的类 分享一个自己写的取中国农历相关数据的类。包含:农历年月日,生肖,星座,年龄,天干,地支等方法 从结亲网 的架构谈起,谈什么架构, 我理解的架构。我想很多人理解的架构应该可能比较 狭义 面向对象的全套“企业微信”api接口的代码实现,网上太多“面向过程”微信api接口的代码,这个开源给需要的人用 解决:百度编辑器UEditor,怎么将图片保存到图片服务器,或者上传到ftp服务器的问题(如果你正在用UE,这篇文章值得你看下) (原创)面向对象的系统对接接口编写。第5篇(完结) (原创)面向对象的系统对接接口编写。第4篇
Revit:二开使用Sqlite保存本地数据,并配合EF6等ORM框架
· 2020-08-26 · via 博客园 - 愚

前人种树,后人乘凉。

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

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

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

using (var conn = new SQLiteConnection(@"data source=E:\公司项目\RevitDevelopment\bin\Debug\Revit.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数据库的功能就实现了。