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

How do I map a Database View? #4561

Closed
gdoron opened this issue Feb 13, 2016 · 20 comments
Closed

How do I map a Database View? #4561

gdoron opened this issue Feb 13, 2016 · 20 comments

Comments

@gdoron
Copy link

gdoron commented Feb 13, 2016

I have an hierarchy table that I'm trying to map with EF 7.
I tried almost everything (except probably the right thing) to map the view I already create in the DB but couldn't make it to work.

This is my model:

public class Post
{
    public int Id { get; set; }
    public string Text { get; set; }
    public DateTime PublishDate { get; set; }
    public DateTime? LastChangedDate { get; set; }
    public String UserId { get; set; }
    public ApplicationUser User { get; set; }
    public int ForumId { get; set; }
    public virtual Forum Forum { get; set; }
    public int? ReplyToPostId { get; set; }
    public Post ReplyToPost { get; set; }
    public virtual List<Post> Replies { get; set; }
}

And this is the view I'm trying to map:

CREATE VIEW HierarchyPosts 
as
WITH    cte ( Id, ParentPostId, Depth ) 
              AS ( SELECT   Id,
                            ReplyToPostId,
                            0 as TheLevel
                   FROM     posts
                   where ReplyToPostId is null
                   UNION ALL 
                   SELECT   pn.Id, 
                            pn.ReplyToPostId,
                            p1.Depth +1
                   FROM     Posts pn
                    INNER JOIN cte AS p1 on p1.Id = pn.ReplyToPostId
                 )
select cte.Id as PostId,ParentPostId, Depth, ForumId, LastChangedDate, PublishDate, ReplyToPostId, Text, U.UserName, u.Id as UserId
from  cte 
INNER JOIN POSTS P ON CTE.ID = P.ID
INNER JOIN USERS u ON U.id = p.UserId

What do I need to write in the DbContext to map HierarchyPosts?

BTW How do I write custom SQL as migration script, for stuff that EF currently doesn't support such as views or for stuff that EF will never support such as partitioning.

BTW2, it would be very helpful if EF 7 would finally be able the handle recursive data natively without requiring views or SP .

Thanks

@gdoron
Copy link
Author

gdoron commented Feb 14, 2016

@divega @rowanmiller , sorry for pinging you on Sunday, but it's quite urgent, I was asked to lecture in a meetup of several early adopting companies about ASP.NET 5 and EF 7, and it should take place this Wednesday...
Thanks!

@ErikEJ
Copy link
Contributor

ErikEJ commented Feb 14, 2016

Mapping Views is not currently supported, and in the Backlog: #827
You can call custom SQL from MIgrations using the Sql method

@gdoron
Copy link
Author

gdoron commented Feb 14, 2016

Thanks @ErikEJ.
I saw issue #827 already, but I didn't imagine there's absolutely no way of mapping views in EF 7 RC (specially that it was opened on Aug 14!).
How can real companies with complicated applications work with EF 7 when in my very simple demo application I created for my lecture there's already a need for views support.

I'm a bit shocked...

@rowanmiller is there any workaround available?

Thanks.

@rowanmiller
Copy link
Contributor

You can tell EF it is a table. Obviously migrations won't create a view for you, but I'm guessing you are mapping to an existing database anyway. If not an existing database then just delete the CreateTable call in migrations and replace with a Sql(string) call to create the view.

Alternatively, you can just use a raw SQL command to select from a view (context.Blogs.FromSql("SELECT * FROM dbo.BlogView")).

@gdoron
Copy link
Author

gdoron commented Feb 16, 2016

Thanks @rowanmiller, the trick with the "table" worked.
So it's not such a great of a deal if there's a relatively simple workaround.

@jlmelis
Copy link

jlmelis commented May 6, 2016

Can you explain the trick?

@rowanmiller
Copy link
Contributor

@jlmelis

You can tell EF it is a table. Obviously migrations won't create a view for you, but I'm guessing you are mapping to an existing database anyway. If not an existing database then just delete the CreateTable call in migrations and replace with a Sql(string) call to create the view.

Alternatively, you can just use a raw SQL command to select from a view ( context.Blogs.FromSql("SELECT * FROM dbo.BlogView") ).

@jlmelis
Copy link

jlmelis commented May 6, 2016

@rowanmiller Sorry I should have been more clear. What do you mean by "tell EF it is a table"?

@rowanmiller
Copy link
Contributor

@jlmelis just write the code the same as you would when you map to a table. Create an entity, and if the table name it expects by convention doesn't match the name of your view then use DataAnnotations or the Fluent API to specify the "table" name.

@gdoron
Copy link
Author

gdoron commented May 7, 2016

Steps:

  1. Create the POCO-class as you would had it been a table.
  2. Add customization like changing the table name as @rowanmiller suggested.

If you are using migrations
3. Create a migration
4. Remove the create table migration script from the Up method and the drop table from the down.
5. Add the create/drop view in the Up/Down instead if you want to manage the view via migrations.

Note that it's a hack and very annoying when you change the View since you need to repeat all these steps on every change.😢

@audacity76
Copy link

audacity76 commented May 13, 2016

Found out that you can ignore the view table in migrations by using the model builder. But you must only do that when migration is ongoing. (IsMigration Property is defined by myself)

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  if (IsMigration)
    modelBuilder.Ignore<YourViewTable>();
 ...
}

@gdoron gdoron mentioned this issue May 17, 2016
16 tasks
@villecoder
Copy link

@audacity76 Any chance you can share your code or some guidance as to how you're handling IsMigration?

@audacity76
Copy link

@villecoder I added the IsMigration property as static to the DbContext. Default value is set to true. In my startup.cs File in the Configure method I set the property to false because the migration doesn't hit the method.

I'm sure there is a better way but I didn't found one. Maybe someone here? @rowanmiller

@rowanmiller
Copy link
Contributor

@audacity76 that looks like a reasonable approach.

The other option is to let it scaffold the CreateTable call and then just delete it from the scaffolded migration. EF won't try and re-create it since it uses the model snapshot to diff against (rather than the actual schema). The downside is that it would try and migrate and changes you make to the view model, so you may have to delete future migration operations too.

@gdoron
Copy link
Author

gdoron commented Oct 10, 2016

@rowanmiller the problem with the other approache is when deleting the snapshot file and all the migrations (a good approach IMHO for every major version) all those tricks will be lost.

@VladanStojanovic
Copy link

@audacity76 I think the inverted approach would be better - you set the default to false and configure it to true only for migrations. When you do Enable-Migrations, a Configuration.cs class is added to a Migrations folder and you can set your static property from there. If your migration configuration and entities are in the same project - even better - you can declare the IsMigration internal and keep it hidden from the rest of the world.

@paaland
Copy link

paaland commented Feb 23, 2017

I'm involved in a large line-of-business application and we decided to give efcore a try. And daily we regret our choice. I understand the team's desire to create a database agnostic tool. But my guess is that 90% of ef core users are running against MS SQL Server and we want all those SQL server or relational database specific feature that the team does not want to implement.

I feel that ef core is ending up as being useless for all, since it won't commit to what it's supposed to be good at. If you are using relational data I feel that EF 6 and model-first is the only way to go if you want full control over your data. Code first sounds tempting, but how many hours do we need to spend on coaxing the framework to create the table we want it to.

@divega
Copy link
Contributor

divega commented Feb 23, 2017

@paaland thanks for providing this feedback. Could you name specifically what SQL Server or general relational features you want that EF Core doesn't allow you to use? Is it just mapping to views or something else? (If the latter it might make sense to create a new issue. We don't often revisit already closed issues like this one) Also can you explain how EF6 model-first is giving you more control to create the database you want? I would really want to understand if there is anything we can do to reduce the friction and your feedback can help us prioritize future work.

@tombrown571
Copy link

@divega I can't speak for @paaland but I would like to see Views in EF Core, (and EF6 if any work is continuing there) ; also indices with include columns. The underlying problem is performance. Using materialised indexed views can be a great way to improve performance - if only EF could allow this using Code First. I would also like read-only tables (where you can only update in the seed method). We get around some of these issues in EF6 using Sql statements in Up/Down migrations - but it feels hackish - it would be nice to have built-in support for these features.

@divega
Copy link
Contributor

divega commented Apr 13, 2017

@tombrown571 as I mentioned in my previous comment, we don't look often at already closed issues like this one. I would recommend you search for existing issues on the areas you care about and then +1 on them (I believe there are existing issues in our backlog for the majority of the things you mentioned).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests