EnsureCreated.To) and ad-hoc mappings in queries (methods start with From). This obviously affects the existing methods but it would be good to have a coherent story if we want to add more, like a FromTable method you can use directly in queries.Right now what needs to happen when you call two methods like ToTable and ToQuery on the same entity type isn't clear.
In general, it is desirable that when and entity is configured with ToTable, it affects what the query pipeline, the update pipeline and the DDL pipeline for that entity. But when ToQuery is also applied, the simplest and most useful behavior seems to be that the latter will only override what happens with the query pipeline.
Here the defining query references People which is the DbSet<Person> just to add an OrderBy call:
modelBuilder.Entity<Person>().HasNoKey();
modelBuilder.Entity<Person>().ToTable("Guests");
modelBuilder.Entity<Person>().ToQuery(() => this.People.OrderBy(p => p.Name));
Ideally this should mean that:
context.People.ToList() is executed, this will generate the following SQL:SELECT [g]
FROM [Guests] AS [g]
ORDER BY [g].[Name]
For the update pipeline, CUD operations will be generated against Guests.
In the DDL pipeline (Migrations and EnsureCreated) will still create the table Guests. But currently any entity that has a defining query configured is ignored by the DDL pipeline. That is, the table won't be created, and there is no way to override that.
There have been similar request from customers to be able to compose ToTable and ToView, so that if both are used together, ToView only applies to the query pipeline, and ToTable to the update pipeline and DDL.
## Proposal
a. We slightly bend the semantics of To as used in APIs like ToTable to specify:
b. We start adding From*, e.g. FromSql, FromTable, FromQuery, FromView that are used only to configure the way instances of a type should be retrieved (e.g. the mapping used by the query pipeline), without affecting the other two aspects.
c. We add the ability to configuring a null mapping, either through a new API or through calling ToTable(null) .
d. We add public API to ignore parts of the model in the DDL pipeline (#2725). That is, so that elements can be configured as declared but not defined in the EF Core model. E.g. IsExtern() or whatever name we choose.
e. We decide what to do with the other existing To* methods on a case-by-case basis:
ToView which doesn't allow a definition of the view to be passed, implies by convention to use the same object for the update pipeline and to ignore the object for the purpose of migrations (e.g. IsExtern(true)).f. For store procedure mapping, also consider how we can leverage the building blocks above. In the past we have got a lot of feedback on the value of having fine-grained mappings, for instance, be able to configure that UPDATEs should go trough a stored procedures while other operations still go through other conventional or explicitly configured mappings. While at the same time, there is value in being able to in a single call configure all CUD operations to stored procedures with by-convention names. But should this override the mapping for the query pipeline, or should we still go by the default table name unless explicitly configured with FromQuery or FromSql?.
Note from team: see case in #19708
Here the defining query references People which is the DbSet
just to add an OrderBy call: modelBuilder.Entity
().HasNoKey();
modelBuilder.Entity().ToTable("Guests");
modelBuilder.Entity().ToQuery(() => this.People.OrderBy(p => p.Name));
Ideally this should mean that:For the query pipeline, when context.People.ToList() is executed, this will generate the following SQL:
SELECT [g].[Name]
FROM [Guests] AS [g]
ORDER BY [g].[Name]
For me, it does not make sense that: context.People.ToList() will return a collection of string?
It makes sense for me this will generate the following SQL:
SELECT [g]
FROM [Guests] AS [g]
ORDER BY [g].[Name]
Did I miss something?
@alugili - It was mistake. Thanks for pointing out. I updated first post.
I don't think there's a need to have separate From* methods. As I've shown for the relational model we can establish a clear priority order that allows to have different mappings for migrations, query and updates simultaneously. The only case it wouldn't allow is to have an entity type mapped to a table for one aspect and not having any mapping at all for other aspects.
Here's a counter-proposal that just builds on existing API to add more mapping functionality:
```C#
modelBuilder
.Entity
.ToTable("Blogs", excludedFromMigrations: false)
.ToView("BlogsView", definitionSql:null, updatable: true)
.FromSql("select * from Blogs")
.ToFunction("GetBlogs", f => f.HasName("get_blogs"))
.ToStoredProcedures(s =>
s.Update(u => u.HasName("modify_blog")
.Parameter(b => b.BlogId, "blog_id")
.Parameter(b => b.Name, "blog_name")
.Parameter(b => b.Url, "blog_url")
.RowsAffectedParameter("rows_affected"))
.Delete(d => d.HasName("delete_blog")
.Parameter(b => b.BlogId, "blog_id"))
.Insert(i => i.HasName("insert_blog")
.Parameter(b => b.Name, "blog_name")
.Parameter(b => b.Url, "blog_url")));
## TPH:
```C#
modelBuilder.Entity<Blog>()
.ToTable("Blogs")
.HasDiscriminator<string>("blog_type")
.HasValue<Blog>("blog_base")
.HasValue<RssBlog>(null)
.HasNotNull<OtherBlog>();
```C#
modelBuilder.Entity
modelBuilder.Entity
## TPC:
```C#
modelBuilder.Entity<Blog>().ToTable("Blogs")
.InheritPropertyMapping(false)
.Property(b => b.BlogId).HasColumnName("ID");
modelBuilder.Entity<RssBlog>().ToTable("RssBlogs")
.ToTable("RssBlogs", bt =>
{
bt.Property(b => b.BlogId).HasColumnName("id");
});
```C#
modelBuilder.Entity
{
b.ToTable("Blogs");
});
modelBuilder.Entity<RssBlog>(b =>
{
b.ToTable("Blogs");
b.HasOne(o => o.Blog).WithOneRequired()
.HasForeignKey<RssBlog>(o => o.BlogId);
});
## Entity splitting:
```C#
modelBuilder.Entity<Blog>(bb =>
{
bb.ToTable("Blogs")
bb.Property(b => b.BlogId).HasColumnName("ID");
bb.Property(b => b.Name).HasColumnName("NAME");
bb.ToTable("RssBlogs", m =>
{
m.Property(b => b.BlogId).HasColumnName("Id");
m.Property(b => b.Url);
})
};
@AndriySvyryd Similar question here--how much can I do end-to-end in preview 2?
/cc @JeremyLikness
@ajcvickers Same, only model building and migrations are done so far
Most helpful comment
I don't think there's a need to have separate
From*methods. As I've shown for the relational model we can establish a clear priority order that allows to have different mappings for migrations, query and updates simultaneously. The only case it wouldn't allow is to have an entity type mapped to a table for one aspect and not having any mapping at all for other aspects.Here's a counter-proposal that just builds on existing API to add more mapping functionality:()
```C#
modelBuilder
.Entity
.ToTable("Blogs", excludedFromMigrations: false)
.ToView("BlogsView", definitionSql:null, updatable: true)
.FromSql("select * from Blogs")
.ToFunction("GetBlogs", f => f.HasName("get_blogs"))
.ToStoredProcedures(s =>
s.Update(u => u.HasName("modify_blog")
.Parameter(b => b.BlogId, "blog_id")
.Parameter(b => b.Name, "blog_name")
.Parameter(b => b.Url, "blog_url")
.RowsAffectedParameter("rows_affected"))
.Delete(d => d.HasName("delete_blog")
.Parameter(b => b.BlogId, "blog_id"))
.Insert(i => i.HasName("insert_blog")
.Parameter(b => b.Name, "blog_name")
.Parameter(b => b.Url, "blog_url")));
TPT:
```C#().ToTable("Blogs");().ToTable("RssBlogs");
modelBuilder.Entity
modelBuilder.Entity
Table splitting:
```C#(b =>
modelBuilder.Entity
{
b.ToTable("Blogs");
});