【问题标题】:DbContext doesn't release SQLite databaseDbContext 不发布 SQLite 数据库
【发布时间】:2013-06-07 08:34:28
【问题描述】:

首先,这是我的意图:

  1. 在 SQLite 上创建 DbContext
  2. 对其进行读写
  3. 关闭上下文
  4. 将文件移动到其他位置

第 1-3 点非常有效。当我尝试移动数据库时,问题就开始了。我收到一条错误消息:

'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 重构


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 完成。你需要更多吗?
  • 什么是SqlLiteContext?它是否封装了实体框架上下文?
  • 一个想法:有没有类似 SQLite 的连接池的东西?在这种情况下,请关闭它。
  • 您应该更喜欢使用“使用”而不是手动调用 Dispose。如果有任何抛出(初始化程序和 Dispose 之间的任何调用),它将不会通过 SqlLiteContext 调用 Dispose。尝试一下,看看该行为是否与直接调用 Dispose 不同——它对如何调用内部受保护方法 Dispose(bool) 的解析可能不同。

标签: c# sqlite entity-framework-4 filelock file-move


【解决方案1】:

我找到了另一个具有相同问题的主题。重构代码后,我添加了

GC.Collect();

解除了文件锁定,我可以移动文件了。

见:https://stackoverflow.com/a/14151917/2462736

【讨论】:

  • 为了确保 GC 真正同步地完成它的工作,也调用GC.WaitForPendingFinalizers()
【解决方案2】:

想到两件事:

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

数据源=e:\mydb.db;Version=3;Pooling=False;

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

【讨论】:

  • 感谢提示。我重构了我的代码,但数据库锁仍然没有消失。我将我的新代码添加到顶部的问题中。通过将 'pooling=false' 添加到数据源并没有帮助......我真的很沮丧...... :(
  • 谢谢,Pooling=false 解决了另一个进程使用的文件的问题
猜你喜欢
  • 2017-03-13
  • 1970-01-01
  • 2019-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多