Have you ever worked with Entity Framework or other ORM and got it NotSupportedException
? Many people have received :
InvalidOperationException: Error generated for warning 'Microsoft.EntityFrameworkCore.Query.QueryClientEvaluationWarning: The LINQ expression could not be translated and will be evaluated locally.'
Mark Seaman strongly believes that, with one exception, all existing implementations violate LSP . He is even willing to send a free copy of his book to the first reader to point him to a real, public implementation IQueryable<T>
that can accept any expression and not throw an exception. For nine years, the book has not found its owner :)
- Hi Mark,
I am writing a blog post that refers to your artticle. I am wondering if you have ever sent a free copy of your book to someone. Presumably not:)- Hi Maxim
That’s right: I haven’t.
Regards
Mark Seemann
. , ToListAsync
. ORM. , IQueryable<T>
API? , — «».
ToListAsync
ToListAsync
. . IQueryable<TSource>
IAsyncEnumerable<TSource>
AsAsyncEnumerable
:
public static async Task<List<TSource>> ToListAsync<TSource>(
[NotNull] this IQueryable<TSource> source,
CancellationToken cancellationToken = default)
{
var list = new List<TSource>();
await foreach (var element in source.AsAsyncEnumerable().WithCancellation(cancellationToken))
{
list.Add(element);
}
return list;
}
public static IAsyncEnumerable<TSource> AsAsyncEnumerable<TSource>(
[NotNull] this IQueryable<TSource> source)
{
Check.NotNull(source, nameof(source));
if (source is IAsyncEnumerable<TSource> asyncEnumerable)
{
return asyncEnumerable;
}
throw new InvalidOperationException(CoreStrings.IQueryableNotAsync(typeof(TSource)));
}
- . , ToListAsync
Task.FromResult
, .
IQueryable
public API?
IQueryable
Entity Framework / NHibernate / Linq2Db . ORM. ORM — .
Linq2Db Entity Framework , , .
, , . « API»?
Out Of Process
, IQueryable
— . GraphQl OData. , , . , rest-like API , . Query Objects
.
In process
IQueryable
, , , , . , , . à la DataQueryHandler
CQRS/Vertical Slices. IQueryable
— , .
IQueryable
, IQueryable
. , . :
public enum UserType: byte
{
Regular,
Vip
}
public class User : IdentityUser
{
[NotMapped]
public int Age { get; set; }
public Organization Organization { get; set; }
public UserType UserType { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
}
— , :
public static class Demo
{
public static bool Filter(User user) =>
user.FirstName.StartsWith("");
//
public static object Works(IEnumerable<User> users) =>
users
.Where(x => Filter(x))
.ToList();
//
public static object ThrowsException(IQueryable<User> users) =>
users
.Where(x => Filter(x))
.ToList();
// ,
public static object WorksAgain(IQueryable<User> users) =>
users
.Where(x => x.FirstName.StartsWith(""))
.ToList();
}
, « ( )» LINQ- , — , visitor, . .
, Q&A DotNext Moscow 2020, BCL. , . , ORM «» . SQL Filter
? — « ». .
public static class DbFunctions
{
public static bool Filter(User user) =>
user.FirstName.StartsWith("");
}
//...
users
.Where(DbFunctions.Filter)
.ToList();
,
public static object Exception (IQueryable<User> users) =>
users
.Where(x => x.FirstName.StartsWith(""))
.ToList();
StartsWith
Contains
, SQL : LIKE “%”
LIKE “%%”
, . , BCL . , :
//
public static object EnumException (IQueryable<User> users) =>
users
.Select(x => x.UserType.ToString())
.Distinct()
.ToList();
//
public static object EnumWorks (IQueryable<User> users) =>
users
.Select(x => ((byte)x.UserType).ToString())
.Distinct()
.ToList();
, Underlying Type Enum
ToString
Convert
.
,MethodCallExpression
, — . , () ( Expression.Convert) , . , DLR, runtime. , , ORM , .
IQueryable
``IQueryable```? . ORM , .. :
select tmp.* from (select from * table) as tmp
: IQueryable
DbContext
. , Task.WhenAll
, IQueryable
, IQueryable
, .
. , « Query Provider ». , :
FullName => FirstName + " " + LastName
,
FullName => $"{FirstName} {LastName}"
[NotMapped]
. , :
public static object NotMappedException (IQueryable<User> users) =>
users
.Where(x => x.Age > 18)
.ToList();
, , , IQueryable
, , , .
, . EF Core ( EF 6, , ):
public static object ConstructorWorks (IQueryable<User> users) =>
users
.Select(x => x.FullName)
.ToList();
:
public static object ConstructorException (IQueryable<User> users) =>
users
.Where(x => x.FullName.StartsWith(""))
.Select(x => x.FullName)
.ToList();
«» EF Core. Select , x => x.FullName
. «» , :
public static object OrganizationWorksOrNot (IQueryable<User> users) =>
users
.Select(x => new { x.Organization.FullName })
//.Where(x => x.FullName.StartsWith(""))
.ToList();
, , . , - Select
. Lazy Loading Include
.
Include
Select
— , , change tracker ( ). ,Include
Lazy Loading « In Defense of Lazy Loading», - DDD-.
Use IQueryable
in internal APIs is okay, but it comes with surprises. What is more: pros or cons - everyone decides for himself. Share in the comments your experience of exciting untranslated query debugging. Let's add interesting stories to the article to make life a little easier for C # programmers.