我做类似的事情:
public class MyDbContext : DbContext {
public MyDbContext(bool readOnlyFlag) {
// Monitor.Enter(theLock); // needed ??
this.readOnlyFlag = readOnlyFlag;
// Database.EnsureCreated(); // needed ??
}
public DbSet<MyData> MyData { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
string connectionString = "Data Source=C:\\mydb.db;";
if (readOnlyFlag) connectionString += "Mode=ReadOnly;";
optionsBuilder.UseSqlite(connectionString);
}
public override void Dispose() {
// Database.CloseConnection(); // needed ??
base.Dispose();
// Monitor.Exit(theLock); // needed ??
}
readonly bool readOnlyFlag;
// readonly object theLock = new object(); // needed ??
}
然后:
using (var dbc = new MyDbContext(true)) {
dbc.MyData.Where( ... code
}
我从多个并发线程中调用此类代码以运行不同的查询..(在.Net Core 3.0控制台应用程序中)
问题:
如果我正确理解,using
块开始时将打开数据库文件,结束时将关闭它。在每个查询中关闭和打开文件似乎效率很低,但是我找不到任何关于保持单例MyDbContext
(例如,在Program
类中并重用它是否可以的参考)?
如果我可以重用MyDbContext
,那么应该在查询周围使用锁吗?
通常,我需要使用上面提到的Monitor
来确保查询不会并发运行吗?我看过帖子说Sqlite需要这个吗?
我需要打{{1}}吗?似乎没有它就可以正常工作,但是我已经看过上面提到的帖子了吗?
Sqlite是否需要Database.EnsureCreated()?
谢谢!
答案 0 :(得分:1)
您可以将DbContext与Sqlite多线程一起使用。通常,应将DbContext用作每个请求的实例,因为DbContext并非线程安全的,因此一次提交不应影响其他提交。
正如sqlite网站上提到的,它支持mutltithreading:
SQLite支持三种不同的线程模式:
单线程。在这种模式下,所有互斥锁都被禁用,SQLite被禁用。 一次不能在多个线程中使用是不安全的。
多线程。在这种模式下,SQLite可以安全地被多个用户使用 线程,前提是不使用单个数据库连接 同时在两个或多个线程中。
序列化。在序列化模式下,SQLite可以安全地被多个用户使用 线程无限制。
可以在编译时选择线程模式(当使用SQLite时) 库是从源代码编译的)或在启动时(当 打算使用SQLite的应用程序正在初始化)或在运行时 (在创建新的SQLite数据库连接时)。通常 讲,运行时优先于开始时间和开始时间优先 编译时。除非单线程模式不能一次覆盖 选择。
默认模式是序列化的。
https://www.sqlite.org/threadsafe.html
我也建议您看看这个SQLite Concurrent Access和这个Can I read and write to a SQLite database concurrently from multiple connections?。
根据上述文章,sqlite writes甚至锁定了整个文件的读取。在互联网上,一些用户建议明确锁定代码以进行写操作。
但是新版本的sqlite具有称为WAL的功能。
WAL模式的第二个优点是作者不会阻止读者 和读者不要阻挡作家。这基本上是对的。但是那里 在一些晦涩的情况下,针对WAL模式数据库的查询可以 返回SQLITE_BUSY,因此应为此准备应用程序 偶然。
Sqlite本身说,即使是多个进程的并发访问也可以由sqlite处理。
如果您的应用程序需要大量并发,那么您 应该考虑使用客户端/服务器数据库。但是经验 表明大多数应用程序所需的并发性比其应用程序少 设计师想象。 当SQLite尝试访问被另一个进程锁定的文件时, 默认行为是返回SQLITE_BUSY。
可能需要在应用程序本身中处理。
答案 1 :(得分:1)
您确定您是数据的唯一用户吗?换句话说,您确定在dbContext的两次使用之间数据不会改变吗?
此外:您确定您的dbContext将始终以这种方式使用,还是将来可能将此dbContext连接到真实数据库?
如果您的线程现在和将来将是唯一的用户,则重用DbContext不会有太大危害。但是,请记住,在处置dbContext之前,不能保证确实写入了数据。此外:您的dbContext会将所有提取的数据保留在本地内存中,因此过一会儿您会将完整的数据库保存在本地内存中。
考虑使用存储库模式,您可以在其中隐藏数据的持久存储方式,该存储库模式可以更多地了解存储库的用途,并且可以更明智地决定要保留哪些数据以及要查询哪些数据新的dbContext创建数据库。
例如,如果您有一个包含学校,学生和教师的数据库,并且经常查询他们的数据,但是很少查询退休教师的数据和研究生的数据,则存储库可以保留所有获取的未退休/已毕业的数据老师/学生在内存中,并且仅创建一个新的dbContext来获取未知数据,获取退休/毕业数据或更新数据库
interface IRepositorySet<Tentity> : IEnumerable<Tentity>
where Tentity : class, new()
{
Tentity Add(Tentity entity);
Tentity Update(Tentity entity);
Tentity Delete(Tentity entity);
}
interface ISchoolsRepository
{
// for simple queries / add / update / remove only
IRepositorySet<School> Schools {get;}
IRepositorySet<Teacher> Teachers {get;}
IRepositorySet<Student> Students {get;}
}
RepositorySet知道何时需要数据时要创建哪个dbContext。所有经常提取的项目都将保存在带有主键的字典中。
在创建字典后,Dictionary中将填充所有主键,并且其值为null,表示尚未提取该项目。
当请求数据时,RepositorySet首先从字典中获取数据。所有仍为空值的项目将从新的dbContext中获取并放入字典中。
请注意,这不适用于海量数据。仅当您认为可以将所有提取的数据保留在内存中时,才考虑使用此解决方案。但是再说一遍:保持dbContext打开还将把所有获取的数据保留在内存中。