В нашем решении мы используем пакет Microsoft.EntityFrameworkCore.Cosmos 3.1.1
для выполнения операций с нашими базами данных и контейнерами в космосе в Azure. У нас довольно простая объектная структура. Объект связывается со списком других объектов.
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;
}
}
Мы настроили DbContext
для EntityFramework следующим образом:
builder.Entity<ExampleEntity>().ToContainer(nameof(ExampleEntity));
builder.Entity<ExampleEntity>().HasKey(p => p.id);
builder.Entity<ExampleEntity>().OwnsMany(p => p.SubEntities);
Структура 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. Именно здесь начинаются наши проблемы.
У нас есть универсальный репозиторий c, где метод 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?