Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What's new: queries #4058

Merged
merged 2 commits into from
Sep 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
451 changes: 450 additions & 1 deletion entity-framework/core/what-is-new/ef-core-7.0/whatsnew.md

Large diffs are not rendered by default.

125 changes: 125 additions & 0 deletions samples/core/Miscellaneous/NewInEFCore7/GroupByEntityTypeSample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
namespace NewInEfCore7;

public static class GroupByEntityTypeSample
{
public static Task GroupBy_entity_type_SqlServer()
{
PrintSampleName();
return QueryTest<BookContextSqlServer>();
}

public static Task GroupBy_entity_type_Sqlite()
{
PrintSampleName();
return QueryTest<BookContextSqlServer>();
}

public static Task GroupBy_entity_type_InMemory()
{
PrintSampleName();
return QueryTest<BookContextInMemory>();
}

private static async Task QueryTest<TContext>()
where TContext : BookContext, new()
{
await using (var context = new TContext())
{
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

var toast = new Author { Name = "Toast" };
var alice = new Author { Name = "Alice" };

await context.AddRangeAsync(
new Book { Author = alice, Price = 10 },
new Book { Author = alice, Price = 11 },
new Book { Author = toast, Price = 12 },
new Book { Author = toast, Price = 13 },
new Book { Author = toast, Price = 14 });

await context.SaveChangesAsync();
}

await using (var context = new TContext())
{
#region GroupByEntityType

var query = context.Books
.GroupBy(s => s.Author)
.Select(s => new { Author = s.Key, MaxPrice = s.Max(p => p.Price) });

#endregion

await foreach (var group in query.AsAsyncEnumerable())
{
Console.WriteLine($"Author: {group.Author.Name}; MaxPrice = {group.MaxPrice}");
}
}

await using (var context = new TContext())
{
#region GroupByEntityTypeReversed
var query = context.Authors
.Select(a => new { Author = a, MaxPrice = a.Books.Max(b => b.Price) });
#endregion

await foreach (var group in query.AsAsyncEnumerable())
{
Console.WriteLine($"Author: {group.Author.Name}; MaxPrice = {group.MaxPrice}");
}
}
}

private static void PrintSampleName([CallerMemberName] string? methodName = null)
{
Console.WriteLine($">>>> Sample: {methodName}");
Console.WriteLine();
}

public abstract class BookContext : DbContext
{
public DbSet<Book> Books => Set<Book>();
public DbSet<Author> Authors => Set<Author>();

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();
}

public class BookContextSqlServer : BookContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> base.OnConfiguring(
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Books"));
}

public class BookContextSqlite : BookContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> base.OnConfiguring(
optionsBuilder.UseSqlite("Data Source = books.db"));
}

public class BookContextInMemory : BookContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> base.OnConfiguring(
optionsBuilder.UseInMemoryDatabase(nameof(BookContextInMemory)));
}

public class Author
{
public int Id { get; set; }
public string Name { get; set; } = default!;
public ICollection<Book> Books { get; } = new List<Book>();
}

public class Book
{
public int Id { get; set; }
public Author Author { get; set; } = default!;
public int Price { get; set; }
}
}
107 changes: 107 additions & 0 deletions samples/core/Miscellaneous/NewInEFCore7/GroupByFinalOperatorSample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
namespace NewInEfCore7;

public static class GroupByFinalOperatorSample
{
public static Task GroupBy_final_operator_SqlServer()
{
PrintSampleName();
return QueryTest<BookContextSqlServer>();
}

public static Task GroupBy_final_operator_Sqlite()
{
PrintSampleName();
return QueryTest<BookContextSqlServer>();
}

public static Task GroupBy_final_operator_InMemory()
{
PrintSampleName();
return QueryTest<BookContextInMemory>();
}

private static async Task QueryTest<TContext>()
where TContext : BookContext, new()
{
await using (var context = new TContext())
{
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

var toast = new Author { Name = "Toast" };
var alice = new Author { Name = "Alice" };

await context.AddRangeAsync(
new Book { Author = alice, Price = 10 },
new Book { Author = alice, Price = 10 },
new Book { Author = toast, Price = 12 },
new Book { Author = toast, Price = 12 },
new Book { Author = toast, Price = 14 });

await context.SaveChangesAsync();
}

await using (var context = new TContext())
{
#region GroupByFinalOperator
var query = context.Books.GroupBy(s => s.Price);
#endregion

await foreach (var group in query.AsAsyncEnumerable())
{
Console.WriteLine($"Price: {group.Key}; Count = {group.Count()}");
}
}
}

private static void PrintSampleName([CallerMemberName] string? methodName = null)
{
Console.WriteLine($">>>> Sample: {methodName}");
Console.WriteLine();
}

public abstract class BookContext : DbContext
{
public DbSet<Book> Books => Set<Book>();
public DbSet<Author> Authors => Set<Author>();

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();
}

public class BookContextSqlServer : BookContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> base.OnConfiguring(
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Books"));
}

public class BookContextSqlite : BookContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> base.OnConfiguring(
optionsBuilder.UseSqlite("Data Source = books.db"));
}

public class BookContextInMemory : BookContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> base.OnConfiguring(
optionsBuilder.UseInMemoryDatabase(nameof(BookContextInMemory)));
}

public class Author
{
public int Id { get; set; }
public string Name { get; set; } = default!;
}

public class Book
{
public int Id { get; set; }
public Author Author { get; set; } = default!;
public int? Price { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
namespace NewInEfCore7;

public static class GroupJoinFinalOperatorSample
{
public static Task GroupJoin_final_operator_SqlServer()
{
PrintSampleName();
return QueryTest<GroupJoinContextSqlServer>();
}

public static Task GroupJoin_final_operator_Sqlite()
{
PrintSampleName();
return QueryTest<GroupJoinContextSqlServer>();
}

public static Task GroupJoin_final_operator_InMemory()
{
PrintSampleName();
return QueryTest<GroupJoinContextInMemory>();
}

private static async Task QueryTest<TContext>()
where TContext : GroupJoinContext, new()
{
await using (var context = new TContext())
{
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

var toast = new Customer { Name = "Toast" };
var alice = new Customer { Name = "Alice" };

await context.AddRangeAsync(
new Order { Customer = alice, Amount = 10 },
new Order { Customer = alice, Amount = 10 },
new Order { Customer = toast, Amount = 12 },
new Order { Customer = toast, Amount = 12 },
new Order { Customer = toast, Amount = 14 });

await context.SaveChangesAsync();
}

await using (var context = new TContext())
{
#region GroupJoinFinalOperator
var query = context.Customers.GroupJoin(
context.Orders, c => c.Id, o => o.CustomerId, (c, os) => new { Customer = c, Orders = os });
#endregion

await foreach (var group in query.AsAsyncEnumerable())
{
Console.WriteLine($"Customer: {group.Customer.Name}; Count = {group.Orders.Count()}");
}
}

await using (var context = new TContext())
{
var query = context.Customers
.GroupJoin(
context.Orders,
o => o.Id,
bt => bt.CustomerId,
(o, bt) => new { Customer = o, BotTasks = bt, });

await foreach (var group in query.AsAsyncEnumerable())
{
Console.WriteLine($"Customer: {group.Customer.Name}; Count = {group.BotTasks.Count()}");
}
}

await using (var context = new TContext())
{
var query =
from customer in context.Customers
join order in context.Orders on customer.Id equals order.CustomerId into orderDetails
select new CustomerWithNavigationProperties { Customer = customer, Orders = orderDetails.ToList() };

await foreach (var group in query.AsAsyncEnumerable())
{
Console.WriteLine($"Customer: {group.Customer.Name}; Count = {group.Orders.Count()}");
}
}
}

private static void PrintSampleName([CallerMemberName] string? methodName = null)
{
Console.WriteLine($">>>> Sample: {methodName}");
Console.WriteLine();
}

public abstract class GroupJoinContext : DbContext
{
public DbSet<Customer> Customers => Set<Customer>();
public DbSet<Order> Orders => Set<Order>();

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();
}

public class GroupJoinContextSqlServer : GroupJoinContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> base.OnConfiguring(
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Customers"));
}

public class GroupJoinContextSqlite : GroupJoinContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> base.OnConfiguring(
optionsBuilder.UseSqlite("Data Source = customers.db"));
}

public class GroupJoinContextInMemory : GroupJoinContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> base.OnConfiguring(
optionsBuilder.UseInMemoryDatabase(nameof(GroupJoinContextInMemory)));
}

public class Customer
{
public int Id { get; set; }
public string Name { get; set; } = default!;
public List<Order> Orders { get; } = new();
}

public class Order
{
public int Id { get; set; }

public int? CustomerId { get; set; }
public Customer? Customer { get; set; }

[Precision(18, 2)]
public decimal Amount { get; set; }
}

public class CustomerWithNavigationProperties
{
public Customer Customer { get; set; } = null!;
public List<Order> Orders { get; set; } = null!;
}
}
Loading