I'm trying to map a SQL View to an entity in EF 5.0 Code First w/Migrations for displaying some basic information on a page without having to query multiple tables for that information (which currently takes ~20 seconds to load. NOT GOOD.). I've heard that it is possible to do, but I haven't been able to figure out or find online a way to properly do so.
EDIT: For a more in-depth look at my solution to this problem, read this blog post on the subject.
Here is my View:
CREATE VIEW [dbo].[ClientStatistics]
AS
SELECT ROW_NUMBER() OVER (Order By c.ID) as Row, c.LegacyID, c.ID, c.ClientName, slc.AccountManager, slc.Network,
(SELECT MAX(CreatedDate) AS Expr1
FROM dbo.DataPeriods
WHERE (ClientID = c.ID)) AS LastDataReceived,
(SELECT MAX(ApprovedDate) AS Expr1
FROM dbo.DataPeriods AS DataPeriods_2
WHERE (ClientID = c.ID)) AS LastApproved,
(SELECT MAX(ReportProcessedDate) AS Expr1
FROM dbo.DataPeriods AS DataPeriods_1
WHERE (ClientID = c.ID)) AS LastReportProcesssed
FROM dbo.Clients AS c INNER JOIN
dbo.SLClients AS slc ON c.ID = slc.ClientID
Here is the entity:
public class ClientStatisticsView
{
[Key]
public int Row { get; set; }
public int LegacyID { get; set; }
public int ClientID { get; set; }
public string ClientName { get; set; }
public string AccountManager { get; set; }
public string Network { get; set; }
public DateTime LastDataReceived { get; set; }
public DateTime LastApproved { get; set; }
public DateTime LastReportProcessed { get; set; }
}
And finally my mapping in DbContext
:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<ClientStatisticsView>().ToTable("ClientStatistics");
base.OnModelCreating(modelBuilder);
}
All of this gives me the following error:
There is already an object named 'ClientStatistics' in the database.
What am I doing wrong? Is there any way to me to accomplish this, or should I be doing something else instead?
Run the Add-Migration InitialCreate command in Package Manager Console. This creates a migration to create the existing schema. Comment out all code in the Up method of the newly created migration. This will allow us to 'apply' the migration to the local database without trying to recreate all the tables etc.
Entity Framework : A Comprehensive Course Views 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.
To use code-first for an existing database, right click on your project in Visual Studio -> Add -> New Item.. Select ADO.NET Entity Data Model in the Add New Item dialog box and specify the model name (this will be a context class name) and click on Add.
One way to create multiple migration sets is to use one DbContext type per provider. Specify the context type when adding new migrations. You don't need to specify the output directory for subsequent migrations since they are created as siblings to the last one.
You have specified that the ClientStatisticsView
entity should be mapped to a table named "ClientStatistics". So entity framework will generate a migration containing an instruction to create that table. But you have independently created that view in the database so to prevent the error you are getting you should remove the CreateTable
instruction from the Up
migration.
I think a better way to do it is to create the view in the migration by running sql like this:
public override void Up()
{
Sql("EXEC ('CREATE View [dbo].[ClientStatistics] AS --etc"
}
public override void Down()
{
Sql(@"IF EXISTS (SELECT
*
FROM sys.views
WHERE object_id = OBJECT_ID(N'dbo.ClientStatistics'))
DROP VIEW dbo.ClientStatistics)")
}
That way your views and tables are specified in one place and you can safely migrate up and down
Reference
http://elegantcode.com/2012/04/12/entity-framework-migrations-tips/
I'm actually working with Entity Framework "Code First" and views, the way I do it is like this:
1) Create a class
[Table("view_name_on_database")]
public class ViewClassName {
// View columns mapping
public int Id {get; set;}
public string Name {get; set;}
// And a few more...
}
2) Add the class to the context
public class ContextName : DbContext {
// Tables
public DbSet<SomeTableClassHere> ATable { get; set; }
// And other tables...
// Views
public DbSet<ViewClassName> ViewContextName { get; set; }
// This lines help me during "update-database" command
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
// Remove comments before "update-database" command and
// comment this line again after "update-database", otherwise
// you will not be able to query the view from the context.
// Ignore the creation of a table named "view_name_on_database"
modelBuilder.Ignore<ViewClassName>();
}
}
A little bit late but I hope this helps somebody.
If there was a way to ignore the creation of a table named as the view during "update-database" and not-ignore after this that would be great.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With