Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use database Views in a scaffolded DbContext with Entity Framework Core 1.0 (EF7)

Unfortunately Entity Framework Core 1.0 (formerly Entity Framework 7) does not yet support Views, and I'm trying to 'fake' it using a table.

However the scaffolding dotnet dbcontext ef scaffold command doesn't currently recognize or generate views, and I want a single DbContext which allows querying a view and updating of tables. Is there a way to do this?

This is the command I use to scaffold the DbContext:

dotnet ef dbcontext scaffold -c MyStoreContext -o Model "Data Source=(local);Initial Catalog=DBNAME;Integrated Security=True" Microsoft.EntityFrameworkCore.SqlServer --force

(This puts all my model classes in a Model directory, and forces them to be overwritten.)

Note: The reason I actually want to use a View is for GROUP BY logic, which isn't supported either in EF Core 1.0

like image 922
Simon_Weaver Avatar asked May 27 '16 01:05

Simon_Weaver


People also ask

Can you use views for Entity Framework?

Entity Framework : A Comprehensive CourseViews can be used in a similar way as you can use tables. To use view as an entity, first you will need to add database views to EDM. After adding views to your model then you can work with it the same way as normal entities except for Create, Update, and Delete operations.

Does EF core support views?

With EF Core 5, we can introduce views in our DbContext and track the evolution of our views using the built-in database migration mechanism. Models behave as they would when mapped directly to a table to take advantage of default mapping conventions.

When you use the scaffold-DbContext command which parameters are required?

You use the DbContext Scaffold command to generate the model. The command has two required arguments - a connection string and a provider.


2 Answers

For me, my view was in a different schema, so I had to alter an attribute on the model class:

using System.ComponentModel.DataAnnotations.Schema;

namespace API.DataAccess
{
    [Table("MyViewName", Schema = "SomeSchema")]
    public class MyViewName
    {
       //props
    }
}

For completeness, the entity code:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<MyViewName>(entity =>
    {
        entity.HasKey(e => new { e.SomeColumn }).HasName("PK_FAKEKEY");
            entity.Property(e => e.SomeColumn ).IsRequired();
            entity.Property(e => e.SomeColumn2 );
            entity.Property(e => e.SomeColumn3 );
    });
}
like image 64
Coruscate5 Avatar answered Sep 18 '22 16:09

Coruscate5


Here's what I came up with:

I create a partial class that inherits from the DbContext and then add any new logic to it by overriding the OnModelCreating method.

Eventually the EF scaffolder should (I hope) be able to create views for me, so in the interim I'm calling the class MyStoreContext_WithViews so I can do a search and replace at some point to update it.

The database view in this example is called RepeatOrderSummaryView.

I had to add all the columns manually to my view class here (since the scaffolder doesn't support it). That's fine for now.

There is no key on the View, but EF requires a key so I just create a fake one using one of the columns.

namespace MyStore.EF.Model
{
    public partial class MyStoreContext_WithViews : MyStoreContext
    {    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<RepeatOrderSummaryView>(entity =>
            {
                entity.HasKey(e => new { e.FirstOrder_Date })
                    .HasName("PK_FAKEKEY");
            });
        }

        public virtual DbSet<RepeatOrderSummaryView> RepeatOrderSummaryView { get; set; }    
    }

    [Table("RepeatOrderSummaryView")]
    public partial class RepeatOrderSummaryView
    {
        public DateTime OrderDate { get; set; }
        public bool HasShipped { get; set; }
    }
}

I was then able to successfully run this query against the View:

RRStoreContext_WithViews ctx = new RRStoreContext_WithViews();
var data = ctx.RepeatOrderSummaryView.Where(x => x.HasShipped == true);

Since this class just inherits from my generated DbContext (MyStoreContext) I can of course use all the rest of the tables that were scaffolded.

Not tested on updateable views - of course if you try this you'll need a real PK defined.


Because you aren't returning 'real' entities, or necessarily items with a real key defined you should use .AsNoTracking(). I found fewer results in my result set than I expected and it was because it was doing a key match and thinking it already had the object in memory.

Be sure to test carefully to ensure the number of results is as expected - or you're going to run into big problems.

What difference does .AsNoTracking() make?

like image 44
Simon_Weaver Avatar answered Sep 22 '22 16:09

Simon_Weaver