Revit:二开使用Sqlite保存本地数据,并配合EF6等ORM框架
作者:互联网
前人种树,后人乘凉。
在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数据库的功能就实现了。
标签:Sqlite,二开,TEntity,System,virtual,entity,EF6,Data,public 来源: https://www.cnblogs.com/mazhiyuan/p/13563048.html