SQLite EF6异步API引发InvalidCastException

时间:2018-07-19 12:57:08

标签: c# entity-framework sqlite entity-framework-6

我是实体框架的新手,并尝试将其与SQLite结合使用。如果我不使用异步API,则可以进行设置。

在我的简化版本中,只有两个表包含一对多关系。数据库设置和值插入工作正常。查询是个问题。

这是简化的代码:

var connectionStringBuilder = new SQLiteConnectionStringBuilder { DataSource = dbFile, ForeignKeys = true };
using (var connection = new SQLiteConnection { ConnectionString = connectionStringBuilder.ConnectionString })
{
    await connection.OpenAsync();
    // query data
    using (var context = new TestContext(connection, contextOwnsConnection: false))
    {
        var xQuery = context.Xs.Include(item => item.Ys);

        var syncX = xQuery.First(); // works fine
        var asyncX = await xQuery.FirstAsync(); // throws InvalidCastException
    }
}

为什么对FirstAsync()的调用会引发异常,而First()不会?

使用SingleAsync()ToListAsync()也会发生同样的情况。目前,我认为所有异步调用都会发生这种情况。

从查询中删除Include(...)子句将解决此问题,但在访问该属性时强制执行第二次数据库查询。

为避免出现诸如“您正在同一查询对象上调用FirstFirstAsync的提示”:否。如果仅在不先调用同步方法的情况下调用...Async(),仍然会出现问题。这只是为了澄清。

我正在使用WinForms应用程序.Net 4.7.1,EF6(通过通过Nuget添加System.Data.SQLite v1.0.108)。

重现该问题的完整代码:

private async void OnClick(object sender, EventArgs e)
{
    var dbFile = "test.sqlite";
    if (File.Exists(dbFile)) File.Delete(dbFile);
    SQLiteConnection.CreateFile(dbFile);

    var connectionStringBuilder = new SQLiteConnectionStringBuilder { DataSource = dbFile, ForeignKeys = true };
    using (var connection = new SQLiteConnection { ConnectionString = connectionStringBuilder.ConnectionString })
    {
        await connection.OpenAsync();

        // setup database
        using (var context = new TestContext(connection, contextOwnsConnection: false))
        {
            await context.Database.ExecuteSqlCommandAsync("CREATE TABLE X (Id VARCHAR2 PRIMARY KEY);");
            await context.Database.ExecuteSqlCommandAsync("CREATE TABLE Y (Id VARCHAR2 PRIMARY KEY, XId VARCHAR2 NOT NULL, FOREIGN KEY (XId) REFERENCES X(Id));");

            var x0 = new X { Id = "X0" };
            var y0 = new Y { Id = "Y0", XId = x0.Id }; // Currently I don't know why using the navigation property 'X = x0' isn't working but the XId will work.
            var y1 = new Y { Id = "Y1", XId = x0.Id }; // Currently I don't know why using the navigation property 'X = x0' isn't working but the XId will work.

            x0.Ys.Add(y0);
            x0.Ys.Add(y1);

            context.Xs.Add(x0);
            context.Ys.Add(y0); // not needed but for safety :-)
            context.Ys.Add(y1); // not needed but for safety :-)

            await context.SaveChangesAsync();
        }

        // query data
        using (var context = new TestContext(connection, contextOwnsConnection: false))
        {
            var xQuery = context.Xs.Include(item => item.Ys);

            var syncX = xQuery.First(); // works fine
            var asyncX = await xQuery.FirstAsync(); // throws InvalidCastException
        }
    }
}

使用以下模型类:

public class TestContext : DbContext
{
    public TestContext(DbConnection existingConnection, bool contextOwnsConnection = true) :
        base(existingConnection, contextOwnsConnection)
    {
    }

    public DbSet<X> Xs { get; set; }

    public DbSet<Y> Ys { get; set; }
}

[Table("X")]
public class X
{
    public X()
    {
        // ReSharper disable once VirtualMemberCallInConstructor
        this.Ys = new HashSet<Y>();
    }

    [Column("Id")]
    [Key, Required]
    public string Id { get; set; }

    public virtual ICollection<Y> Ys { get; set; }
}

[Table("Y")]
public class Y
{
    [Column("Id")]
    [Key, Required]
    public string Id { get; set; }

    [Column("XId")]
    [Required]
    public string XId { get; set; }

    [ForeignKey("XId")]
    public virtual X X { get; set; }
}

2 个答案:

答案 0 :(得分:0)

对不起,我无法发表评论。

您的问题非常有趣,我首先想到的是类型不匹配,因为我确切地记得FirstAsync接受IQueryable的内容,如果我确实包含了某些内容,则请求的类型为IIncludeableQueryable,但是我测试了我的猜测和遗憾的是,它通过dot-peek考察了实现,在两种情况下都有效,并且IIncludeableQueryable本身是从IQueryable继承的

        var testQuery = DbContext.Practices.Include(x => x.Facility);
        var testQuery2 = DbContext.Practices.Select(x => x);

        var asyncPracticeCorrectType = await testQuery2.FirstAsync();
        var asyncPractice = await testQuery.FirstAsync();

我通过两种方式获得了实体练习:(

我认为您在SQLite方面遇到的问题,因为您的代码看起来正确

答案 1 :(得分:0)

我尝试使用实体框架核心而不是实体框架6来解决该问题。

在我的选择中,这是entitiy框架6或sqlite提供程序中的错误。