dotnet / dotnet/EntityFramework.Docs

Request for better Keyless Type documentation

Open
#2,124 1 comment 0 reactions 0 assignees View on GitHub
area-model-building consider-for-next-release
Dominant language
Mermaid
Stars
1.7k
Forks
2k
Avg merge
7d 23h
Merged PRs (30d)
16

Description

I think the documentation of the Keyless Types is too simple.

Things that are not currently documented:
- ToQuery method with lambda
- What is the currently recommended way to use Keyless Types?
- How can keyless types be reused as efficiently as possible?
- What are the (better described) limitations

### query documentation
Currently the documentation only shows how to use the keyless types in conjunction with database views.

```sql
db.Database.ExecuteSqlRaw(
@"CREATE VIEW View_BlogPostCounts AS
SELECT b.Name, Count(p.PostId) as PostCount
FROM Blogs b
JOIN Posts p on p.BlogId = b.BlogId
GROUP BY b.Name");
```
```cs
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity(eb =>
{
eb.HasNoKey();
eb.ToView("View_BlogPostCounts");
eb.Property(v => v.BlogName).HasColumnName("Name");
});
}
```
But how would this example look like if I simply used ToQuery instead of ToView?
My guess would be:
```cs
b.Entity().HasNoKey();
b.Entity().ToQuery(
() => Blogs.Select(blog => blog.Name, blog.Posts.Count));
```
But after several tests I am at the point where I think that this does not (no longer) work.
I have an example here that is very similar to (and based on) the blog post. Just not the posts per blog, but per user.

```cs
b.Entity().HasNoKey();
b.Entity().ToQuery(
() => UserAccounts.Select(
u => new UserBlogStatsView(u.Id, u.BlogPosts.Count())));
```
Unfortunately this ends in the following exception:

> InvalidOperationException: The LINQ expression 'DbSet
.Select(u => new UserBlogStatsView(
u.Id,
DbSet
.Where(f => EF.Property>(u, "Id") != null && EF.Property>(u, "Id") == EF.Property>(f, "PostedByUserId"))
.Count()
))
.Where(u0 => u0.UserId == 1)' 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(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

So, this is a regression?

### View reuse
The next point would be the combination of views in order to reuse a view as often as possible.

I've now tried various ways this might work by now, but the only one that worked was raw SQL - which I like to avoid.

```cs
public class UserProfileView
{
public int Id { get; set; }
public string UserName { get; set; }
public UserBlogStatsView StatsView { get; set; }

public UserProfileView(int id, string userName, UserBlogStatsView statsView)
{
Id = id;
UserName = userName;
StatsView = statsView)
}
}
```

When I try to register this in the ModelBinder, I get the following exception:

> The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'statsView' in 'UserProfileView(int id, string userName, UserBlogStatsView statsView)'.

Here I am relatively sure that this is currently not supported - but it would be nice. But what is the idea of reusing views as often and easily as possible?
If I cannot load the views at the same time ina simple single query, can I at least load the views with multi queries (that refer to one or more entities at the same time)?
```cs
var cq = from user in _dbContext.UserAccounts
select new
{
UserProfile = _dbContext.UserIdentityView.Where(u => user.Id == u.UserId).Single(),
UserBlogStats = _dbContext.UserBlogStatsViews.Where(u => user.Id == u.UserId).Single(),
};

var cr = await cq.FirstOrDefaultAsync(cancellationToken);
```
Currently I only get an exception that the query could not be translated.

So I would be very happy if the documentation on how to use keyless types better and more efficient would be extended.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.