我是实体框架的新手,并尝试将其与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(...)
子句将解决此问题,但在访问该属性时强制执行第二次数据库查询。
为避免出现诸如“您正在同一查询对象上调用First
和FirstAsync
的提示”:否。如果仅在不先调用同步方法的情况下调用...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; }
}
答案 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提供程序中的错误。