【问题标题】:Entity Framework Core and Cosmos DB. Using LINQ expressions to read entities实体框架核心和 Cosmos DB。使用 LINQ 表达式读取实体
【发布时间】:2020-02-14 08:28:42
【问题描述】:

在我们的解决方案中,我们使用包 Microsoft.EntityFrameworkCore.Cosmos 3.1.1 对 Azure 中的 cosmos 数据库和容器进行操作。我们有一个相当简单的对象结构。一个对象包含其他对象的列表。

public class ExampleEntity : Entity
{
    public string TestProperty { get; set; }
    public IEnumerable<SubEntity> SubEntities { get; set; }

    protected override IEnumerable<object> GetEqualityComponents()
    {
            yield return TestProperty;
    }
}

public class SubEntity : Entity
{
    public bool IsActive { get; set; }

    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return IsActive;
    }
}

我们已经为 EntityFramework 配置了DbContext,如下所示:

builder.Entity<ExampleEntity>().ToContainer(nameof(ExampleEntity));
builder.Entity<ExampleEntity>().HasKey(p => p.id);
builder.Entity<ExampleEntity>().OwnsMany(p => p.SubEntities);

cosmos中的json结构是这样的:

{
    "id": "51099fa9-5d71-4181-93b1-2c8cc0482a95",
    "CreatedAt": "2020-02-14T08:11:06.701659Z",
    "Discriminator": "ExampleEntity",
    "TestProperty": "Property1",
    "UpdatedAt": "0001-01-01T00:00:00",
    "SubEntities": [
        {
            "id": "9a120613-c42a-4399-a660-e6228cfce0ad",
            "CreatedAt": "2020-02-14T08:11:06.70457Z",
            "ExampleEntityid": "51099fa9-5d71-4181-93b1-2c8cc0482a95",
            "IsActive": false,
            "UpdatedAt": "0001-01-01T00:00:00"
        },
        {
            "id": "21b86b53-2d6a-4b31-a60b-8d31cfd04734",
            "CreatedAt": "2020-02-14T08:11:06.705145Z",
            "ExampleEntityid": "51099fa9-5d71-4181-93b1-2c8cc0482a95",
            "IsActive": true,
            "UpdatedAt": "0001-01-01T00:00:00"
        }
    ],
    "_rid": "R343APAECLsBAAAAAAAAAA==",
    "_self": "dbs/R343AA==/colls/R343APAECLs=/docs/R343APAECLsBAAAAAAAAAA==/",
    "_etag": "\"06001f30-0000-0d00-0000-5e46561b0000\"",
    "_attachments": "attachments/",
    "_ts": 1581667867
}

现在,我们要搜索ExampleEntities,其中SubEntities 的布尔值IsActive 设置为true。这就是我们的问题开始的地方。

我们有一个通用存储库,其中 Read 方法如下所示:

/// <summary>
/// Get an entity from the database
/// </summary>
/// <param name="predicate">A predicate to decide which entity to get</param>
/// <param name="children">Child entities to included in the DbSet</param>
/// <returns>All entities that matches the predicate</returns>
public async Task<IEnumerable<TEntity>> ReadAsync(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] children)
{
    var dbSet = _dbContext.Set<TEntity>();
    children.ToList().ForEach(p => dbSet.Include(p));

    var entities = dbSet.Where(predicate);
    return await entities.ToListAsync();
}

在 IntegrationTest 中使用如下代码:

[Test]
public async Task EntityFramework_Should_Return_Object_Based_On_Property_In_SubEntity()
{
    var uow = Container.Resolve<IUnitOfWork<ExampleEntity>>();
    var entity1 = new ExampleEntity
    {
        TestProperty = "Property1",
        SubEntities = new List<SubEntity>
        {
            new SubEntity
            {
                IsActive = false
            },
            new SubEntity
            {
                IsActive = true
            }
        }
    };

    await uow.Repository.CreateAsync(entity1);
    await uow.CommitAsync();

    var readEntity = uow.Repository.ReadAsync(p => p.SubEntities.Any(p => p.IsActive), p => p.SubEntities);
    readEntity.Should().NotBeNull();
}

问题发生在我使用上面存储库中的 Read 方法的这一行:

var readEntity = uow.Repository.ReadAsync(p => p.SubEntities.Any(p => p.IsActive), p => p.SubEntities);

导致以下异常:

System.InvalidOperationException: The LINQ expression 'DbSet<ExampleEntity>
    .Where(e => EF.Property<IEnumerable<SubEntity>>(e, "SubEntities")
        .AsQueryable()
        .Any(o => o.IsActive))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().

我觉得很奇怪,Entity Framework Cosmos 不支持这样的简单查询。显然我可以做一个AsEnumerable(),但是我会从数据库下载所有数据并在客户端而不是数据库端进行过滤,当数据库包含100'000条记录时,这将对性能产生巨大影响。 .

如何重写我的存储库以在数据库端进行此类过滤? Entity Framework Cosmos 有可能吗?

【问题讨论】:

    标签: c# azure entity-framework-core azure-cosmosdb entity-framework-core-3.1


    【解决方案1】:

    根据current limitations Includejoin 尚不支持。

    【讨论】:

    • 感谢分享链接。当使用 cosmos 提供的 SQL API 时,它在任何 ORM 中都不起作用,这真的令人沮丧。我可以在 azure 门户中进行查询并获得我想要的结果。我希望 Entity Framework Core Cosmos 能够实现提供原始 sql 查询的功能。我已经尝试过 FromRawSql,但它没有为 IQueryable 实现。你知道任何其他使用 EF Core 的方法吗?否则我会改用 Cosmonaut 之类的东西..
    • 你有没有找到什么时候加入和包含实现的路线图?在他们的 GitHub 错误/功能跟踪器上找不到任何东西..
    • EF Core 5.0 预览中没有消息 :'(
    • 我不认为该链接与该问题完全相关-在查询嵌入式文档时(明确)不使用 Join() 和 Include(),它们似乎默认包含在内。看起来更像是一个翻译错误,而不是达到已知限制。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-15
    • 2020-05-10
    • 1970-01-01
    • 1970-01-01
    • 2021-07-14
    • 1970-01-01
    相关资源
    最近更新 更多