Я пытаюсь создать своего рода обобщенное выражение c "StartsWith" в EF Core 3.1.5. Управляемый объект выглядит так:
public class MyEntity
{
[System.ComponentModel.DataAnnotations.Key] // key just for the sake of having a key defined, not relevant for the question
public string TopLevelString { get; set; }
public AnotherEntity OtherEntity { get; set; }
}
public class AnotherEntity
{
[System.ComponentModel.DataAnnotations.Key] // key just for the sake of having a key defined, not relevant for the question
public string NestedString { get; set; }
}
Я помещаю его в такой контекст:
public class MyDbContext : DbContext
{
public DbSet<MyEntity> MyEntities { get; set; }
public MyDbContext(DbContextOptions<MyDbContext> options) {}
protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlite("Data Source=sqlitedemo.db");
}
И пытаюсь использовать этот контекст в тестовом классе:
public partial class MyTestClass // this part is just to make the example 100% reproducable
{
// define some minimal examples to work with
private List<MyEntity> testExampleList = new List<MyEntity>()
{
new MyEntity()
{
TopLevelString = "ABC",
OtherEntity = new AnotherEntity(){NestedString = "ABC"}
},
new MyEntity()
{
TopLevelString = "XYZ",
OtherEntity = new AnotherEntity(){NestedString = "XYZ"}
}
};
MyDbContext context;
public MyTestClass()
{
// set up database
var options = new DbContextOptions<MyDbContext>();
this.context = new MyDbContext(options);
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
// add examples from above list
this.context.MyEntities.AddRange(testExampleList);
this.context.SaveChanges();
}
}
Вот то, что я хотел бы сделать как простой фильтр Where:
public partial class MyTestClass // this part works as expected and is just used to illustrate the purpose of below code
{
[Fact]
public void TestFilteringWithoutOwnExpression()
{
Assert.Equal(1, context.MyEntities.Where(x => x.TopLevelString.StartsWith("A")).Count()); // works fine
Assert.Equal(1, context.MyEntities.Where(x => x.OtherEntity.NestedString.StartsWith("A")).Count()); // works, too
}
}
Поскольку до применения предложения where должно произойти какое-то другое c, я попытался оберните его в собственное выражение, например:
public partial class MyTestClass // this part does not work and I don' know why
{
[Fact]
public void TestFilteringWithExpression()
{
Assert.Equal(1, context.MyEntities.MyWhere<MyEntity>(x => x.TopLevelString, "A").Count());
Assert.Equal(1, context.MyEntities.MyWhere<MyEntity>(x => x.OtherEntity.NestedString, "A").Count());
}
}
с MyWhere
, определенным в классе расширения:
public static class IQueryableExtension
{
public static IQueryable<TEntity> MyWhere<TEntity>(this IQueryable<TEntity> query, Expression<Func<TEntity, string>> stringSelector, string searchString)
{
ParameterExpression entityParameter = Expression.Parameter(typeof(TEntity), stringSelector.Parameters.First().Name);
MemberExpression memberExpr = (MemberExpression)(stringSelector.Body);
var searchConstant = Expression.Constant(searchString, typeof(string));
var filterExpression = Expression.Lambda<Func<TEntity, bool>>(
Expression.Call(
memberExpr,
typeof(string).GetMethod(nameof(string.StartsWith), new Type[] { typeof(string) }),
searchConstant),
entityParameter);
query = query.Where(filterExpression);
return query;
}
}
Я видел аналогичные примеры, где вместо MemberExpression используется PropertyExpression используется, но это не удалось для меня, как только я попытался получить доступ не только к MyEntity.TopLevelString
, но и к вложенному MyEntity.AnotherEntity.NestedString
.
Код завершился с ошибкой InvalidOperationException:
Выражение LINQ 'DbSet .Where (m => x.TopLevelString! = Null && "A"! = Null && x.TopLevelString.StartsWith ("A"))' не может быть переведено. Либо перепишите запрос в форме, которая может быть переведена, либо переключитесь на оценку клиента ... +
Как мне настроить общий c и переводимый StartsWith Expression?