DbContext无法释放SQLite数据库。

10

首先,我想要做的是:

  1. 在SQLite上创建DbContext
  2. 从中读取和写入数据
  3. 关闭context
  4. 将文件移动到另一个位置

前三个步骤都可以正常工作。但当我尝试移动数据库时,会出现错误提示:

'The process cannot access the file because it is being used by another process.' 

我该如何解决这个问题?

首先,我创建了一个上下文。我需要在多个方法中使用它,而且我不想每次需要时都创建它。因此,我将其存储为成员变量。

_sqliteContext = new SqlLiteContext(sqliteContextName);

然后我想访问名为 sync 的表,并获取其最新的条目。
var sync = _sqliteContext.Syncs.OrderByDescending(s => s.Date);
_lastSync = sync.Any() ? sync.First().Date : new DateTime(0);

那就这样了。然后我关闭上下文。
_sqliteContext.Dispose();

尝试移动文件。

File.Move(sqliteUploadLocation, sqliteDownloadLocation);

这是我遇到异常的地方。

当我用以下方式将选择内容替换为插入:

var sync = new Sync { Id = Guid.NewGuid().ToString(), Date = DateTime.Now };
_sqliteContext.Syncs.Add(sync);
_sqliteContext.SaveChanges();

这个可以正常工作,我可以移动数据库。有什么想法为什么我的选择不释放锁定?

更新


// Start synchronisation.
new SyncManager(mssqlString, sqliteUploadLocation).Start();

// Move file from upload to download location.
try
{
    File.Move(sqliteUploadLocation, sqliteDownloadLocation);
}
catch (Exception ex)
{
    Console.WriteLine("Moving failed!");
    Console.WriteLine(ex.Message);
}

public void Start()
{
    // Create connection string for the sqlite database.
    const string sqliteContextName = "SqLiteContext";
    var sqliteConnStringSettings = new ConnectionStringSettings
        {
            Name = sqliteContextName,
            ConnectionString = "Data Source=" + _sqliteUploadLocation + ";Version=3;BinaryGUID=False;",
            ProviderName = "System.Data.SQLite"
        };

    // Read configuration, delete available connection strings and add ours.
    var conf = ConfigurationManager.OpenMachineConfiguration();
    var connStrings = conf.ConnectionStrings;
    connStrings.ConnectionStrings.Remove(sqliteContextName);
    connStrings.ConnectionStrings.Add(sqliteConnStringSettings);
    try
    {
        conf.Save(ConfigurationSaveMode.Minimal);
    }
    catch (Exception)
    {
        // Insufficient rights to save.
        return;
    }

    ConfigurationManager.RefreshSection("connectionStrings");

    // Create connection to the sqlite database.
    _sqliteContext = new SqlLiteContext(sqliteContextName);

    // Create connection to the mssql database.
    _mssqlContext = new MsSqlContext(_mssqlConnString);

    // Read last sync date.
    var sync = _sqliteContext.Syncs.OrderByDescending(s => s.Date);
    _lastSync = sync.Any() ? sync.First().Date : new DateTime(0);

    // Synchronize tables.
    //SyncTablePerson();
    //SyncTableAddressAllocation();

    // Creates an entry for this synchronisation.
    CreateSyncEntry();

    // Release resources.
    _sqliteContext.Dispose();
    _mssqlContext.Dispose();
}

private void CreateSyncEntry()
{
    var sync = new Sync { Id = Guid.NewGuid().ToString(), Date = DateTime.Now };
    _sqliteContext.Syncs.Add(sync);
    _sqliteContext.SaveChanges();
}

更新2


public class SqlLiteContext : Context
{
    public DbSet<Sync> Syncs { get; set; }

    public SqlLiteContext(string connectionString)
        : base(connectionString)
    {
        Database.SetInitializer(new NoOperationStrategy<SqlLiteContext>());
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new PersonConfig());
        modelBuilder.Configurations.Add(new AddressAllocationConfig());
        modelBuilder.Configurations.Add(new AddressConfig());
        modelBuilder.Configurations.Add(new SyncConfig());
    }
}

public class NoOperationStrategy<T> : IDatabaseInitializer<T> where T : DbContext
{
    public void InitializeDatabase(T context)
    {
    }
}

public abstract class Context : DbContext
{
    public DbSet<Person> Persons { get; set; }
    public DbSet<AddressAllocation> AddressAllocations { get; set; }
    public DbSet<Address> Addresses { get; set; }

    protected Context(string connectionString)
        : base(connectionString)
    {
    }
}

使用重构技术


using (var sqliteContext = new SqlLiteContext(_sqliteContextName))
{
    // Read last sync date.
    var sync = sqliteContext.Syncs.Select(s => s).OrderByDescending(s => s.Date);
    var lastSync = sync.Any() ? sync.First().Date : new DateTime(1900, 1, 1);

    using (var mssqlContext = new MsSqlContext(_mssqlConnString))
    {
        SyncTablePerson(sqliteContext, mssqlContext, lastSync);
        SyncTableAddressAllocation(sqliteContext, mssqlContext, lastSync);

        // Save server changes.
        mssqlContext.SaveChanges();
    }

    // Creates an entry for this synchronisation.
    sqliteContext.Syncs.Add(new Sync { Id = Guid.NewGuid().ToString(), Date = DateTime.Now });

    // Save local changes.
    sqliteContext.SaveChanges();
}

你可以展示一下你的实际代码吗?这样所有内容都在同一个块里,更容易看到实际的执行流程,因为上下文生命周期非常重要。 - ken2k
@ken2k 完成了。您需要更多吗? - Teazl
1
你应该优先使用 "using" 而不是手动调用 Dispose。如果在初始化程序和 Dispose 之间的任何调用抛出异常,它将不会调用 SqlLiteContext 上的 Dispose。试试看,看看行为是否与直接调用 Dispose 不同--它调用内部受保护方法 Dispose(bool) 的方式可能不同。 - Matt Kerr
为什么需要移动数据库文件? - Ryan Gates
数据库将通过WCF服务上传到服务器。在那里,它将与MSSQL数据库同步。完成该过程后,数据库将移动到可下载的位置以便再次下载...由于同步过程未连接到WCF服务,并且无法检查同步过程是否已完成,因此我会在同步过程完成后移动文件,以向WCF服务发出同步完成的信号... - Teazl
显示剩余4条评论
2个回答

3

有两件事需要注意:

  1. 确保Visual Studio没有锁定数据库文件。打开服务器资源管理器,如果有连接到该文件的连接,请确保其已关闭或完全删除。
  2. 很可能是连接池持有连接处于开启状态。在你的连接字符串中禁用连接池,如下所示:

Data Source=e:\mydb.db;Version=3;Pooling=False;

正如Matt所指出的那样,你应该使用using语句而不是手动调用dispose。这样,如果出现异常,资源始终会被正确释放。


谢谢你的提示。我重构了我的代码,但是数据库锁仍然存在。我已经将新代码添加到问题的顶部。在数据源中添加“pooling=false”并没有帮助...我真的很沮丧...:( - Teazl
2
谢谢,Pooling=false 解决了文件被另一个进程使用的问题。 - dodbrian
有没有想法为什么 Pooling=false 会让它保持打开状态? - undefined

3

2
为确保垃圾回收同步执行其任务,请同时调用 GC.WaitForPendingFinalizers() - Jack Miller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接