Using Views like tables in Entity Framework Code-first Existing database approach - entity-framework-6

I currently have a working WPF project that is using 2 tables from a sample Northwind database. Using Entity Framework code-first from an existing database these tables are displayed in master-detail format. I would like to use two views instead of the two tables.
I have found several tutorials and have implemented accordingly but for some reason I cannot get the dbcontext to read from the views. I created two views each matching the tables. I modified the class for the view to use [Table("vView")] and assigned a [Key] to create a primary key on the view but still no success. All research shows is to treat the view as if it was a table and all code should be the same. I am using Products and Categories table from Northwind database to test. I created their corresponding views vProducts and vCategories in the Northwind database.
Here is the code that works using the tables Products and Categories generated from the Entity Data Model (code-first):
Category.cs
namespace WPFCodeFirstExisting
{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public class Category
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Category()
{
Products = new HashSet<Product>();
}
public int CategoryID { get; set; }
[Required]
[StringLength(15)]
public string CategoryName { get; set; }
[Column(TypeName = "ntext")]
public string Description { get; set; }
[Column(TypeName = "image")]
public byte[] Picture { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Product> Products { get; set; }
}
}
Products.cs
namespace WPFCodeFirstExisting
{
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public class Product
{
public int ProductID { get; set; }
[Required]
[StringLength(40)]
public string ProductName { get; set; }
public int? SupplierID { get; set; }
public int? CategoryID { get; set; }
[StringLength(20)]
public string QuantityPerUnit { get; set; }
[Column(TypeName = "money")]
public decimal? UnitPrice { get; set; }
public short? UnitsInStock { get; set; }
public short? UnitsOnOrder { get; set; }
public short? ReorderLevel { get; set; }
public bool Discontinued { get; set; }
public virtual Category Category { get; set; }
}
}
ProductContext.cs
namespace WPFCodeFirstExisting
{
using System.Data.Entity;
public partial class ProductContext : DbContext
{
public ProductContext()
: base("name=ProductContext")
{
Database.SetInitializer<ProductContext>(null);
}
public virtual DbSet<Category> Categories { get; set; }
public virtual DbSet<Product> Products { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.Property(e => e.UnitPrice)
.HasPrecision(19, 4);
}
}
}
MainWindows.xaml.cs
public partial class MainWindow : Window
{
public ProductContext _context = new ProductContext();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
System.Windows.Data.CollectionViewSource categoriesViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("categoriesViewSource")));
_context.Categories.Where(x => x.CategoryName.Contains("P")).Load();
categoriesViewSource.Source = _context.Categories.Local;
}
App.config:
<connectionStrings>
<add name="ProductContext" connectionString="data source=ABC;initial catalog=Northwind;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
<add name="ProductContextView" connectionString="data source=ABC;initial catalog=Northwind;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
</connectionStrings>
Now if I modify the code to use 2 views instead:
SELECT * FROM [dbo].[vProducts]
SELECT * from [dbo].[vCategories]
these views just contain the select statement from their respective tables.
vCategory.cs:
namespace WPFCodeFirstExisting
{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("vCategories")]
public class vCategory
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public vCategory()
{
vProducts = new HashSet<vProduct>();
}
[Key]
public int CategoryID { get; set; }
[StringLength(15)]
public string CategoryName { get; set; }
[Column(TypeName = "ntext")]
public string Description { get; set; }
[Column(TypeName = "image")]
public byte[] Picture { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<vProduct> vProducts { get; set; }
}
}
vProducts.cs:
namespace WPFCodeFirstExisting
{
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("vProducts")]
public class vProduct
{
[Key]
public int ProductID { get; set; }
[Required]
[StringLength(40)]
public string ProductName { get; set; }
public int? SupplierID { get; set; }
[ForeignKey("CategoryID")]
public int? CategoryID { get; set; }
[StringLength(20)]
public string QuantityPerUnit { get; set; }
[Column(TypeName = "money")]
public decimal? UnitPrice { get; set; }
public short? UnitsInStock { get; set; }
public short? UnitsOnOrder { get; set; }
public short? ReorderLevel { get; set; }
public bool Discontinued { get; set; }
public virtual vCategory vCategory { get; set; }
}
}
ProductContextView.cs
namespace WPFCodeFirstExisting
{
using System.Data.Entity;
public class ProductContextView : DbContext
{
public ProductContextView()
: base("name=ProductContextView")
{
Database.SetInitializer<ProductContextView>(null);
}
public DbSet<vCategory> vCategories { get; set; }
public DbSet<vProduct> vProducts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<vProduct>()
.Property(e => e.UnitPrice)
.HasPrecision(19, 4);
}
}
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public ProductContextView _context = new ProductContextView();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
System.Windows.Data.CollectionViewSource categoriesViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("categoriesViewSource")));
_context.vCategories.Where(x => x.CategoryName.Contains("P")).Load();
categoriesViewSource.Source = _context.vCategories.Local;
}
When I run using the ProductContextView, These lines do not return any data;
_context.vCategories.Where(x => x.CategoryName.Contains("P")).Load();
categoriesViewSource.Source = _context.vCategories.Local;
I even tried doing something very simple like the following:
var prods = _context.Database.SqlQuery<vCategory>("Select * from dbo.vCategories");
foreach (var p in prods)
{
string test = p.CategoryName.ToString();
}
but prods doesn't return any data either. This test works perfectly fine when using the tables.
I'm hoping to just replace the tables with views using EF. I do not want to use any CRUD operations with the views they are fine being read-only.

What I found is that in code-first if you add a class representing a view as dbSet when the database is seeded it will create what you want to be a view as a table (probably why you are getting no results).
A work around is to create a custom an initializer :
public class CustomDBInitializer : CreateDatabaseIfNotExists<ProductContextView>
{
protected override void Seed(ProductContextView context)
{
base.Seed(context);
}
}
Then after the base seed method use...
context.Database.ExecuteSqlCommand()
...to drop the generated tables and run a SQL script that generates the views to replace them.
You can then use the dbset like a table but it will actually be using the view.
Also not sure why you created to separate contexts.

Related

Metadata class not working in Entity Framework? Ignoring class property

I am trying to ignore a class property when inserting data to database using metadata for the class but it is not working. I am using using EF 6. I have tried both the metadata and partial class are in the same assembly as the classes generated by EF
[NotMapped] and [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
Used a internal sealed class (for metadata) inside my partial class
namespace XeroDataStore.XeroDatabase
{
[MetadataType(typeof(TempAddressMetadata))]
public partial class TempAddress
{
}
[MetadataType(typeof(TempContact.TempContactMetadata))]
public partial class TempContact
{
internal sealed class TempContactMetadata
{
[NotMapped]
public Nullable<System.DateTime> UploadDate { get; set; }
}
}
}
namespace XeroDataStore.XeroDatabase
{
public class TempAddressMetadata
{
[NotMapped]
public Nullable<System.DateTime> UploadDate { get; set; }
}
}
EF Generated Class
namespace XeroDataStore.XeroDatabase
{
public partial class TempAddress
{
public int RowId { get; set; }
public int ClientID { get; set; }
public System.Guid ContactID { get; set; }
public string AddressType { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string AddressLine3 { get; set; }
public string AddressLine4 { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public string AttentionTo { get; set; }
public Nullable<System.DateTime> UploadDate { get; set; }
public virtual TempContact TempContact { get; set; }
}
}
What am I missing here?
Do it using Fluent API to make sure your model classes are POCO and have nothing to do with the data access.
In your data context, OnModelCreating methoed, use the following code to ignore the property
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TempContact>().Ignore(a => a.UploadDate );
}

pass multiple data as a list to View in MVC

How to pass LINQ data to View. I have used below code but it throwing conversion error.
namespace WebApplication1.DataContext
{
public class Repositary
{
public ProductMaster productMaster { get; set; }
public ProductDetail productDetail { get; set; }
}
}`
Action Method:-
public ActionResult ProductMasterWithDetails()
{
Model1 db=new Model1();
Repositary rep = new Repositary();
var varResult= from pm in db.ProductMasters join pd in db.ProductDetails on pm.ProductId equals pd.ProductId select new { pm.ProductId, pm.ProductName, pd.Price, pd.ManufactureBy };
ViewBag.result = varResult.ToList();
return View();
}
Error: Cannot initialize type 'WebApplication1.DataContext.Repositary'
with a collection initializer because it does not implement
'System.Collections.IEnumerable'
C:\Users\msnsh\onedrive\documents\visual studio
2013\Projects\WebApplication1\WebApplication1\Controllers\ProductController.cs 55 141 WebApplication1
In View:
#foreach (var result in ViewBag.result){
}
I have two model classes as follows.
namespace WebApplication1.DataContext
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
[Table("ProductMaster")]
public partial class ProductMaster
{
public ProductMaster()
{
ProductDetails = new HashSet<ProductDetail>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ProductId { get; set; }
[StringLength(50)]
public string ProductName { get; set; }
public virtual ICollection<ProductDetail> ProductDetails { get; set; }
}
}
namespace WebApplication1.DataContext
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
public partial class ProductDetail
{
public int? Price { get; set; }
[StringLength(50)]
public string ManufactureBy { get; set; }
public int? ProductId { get; set; }
[Key]
public int ProdDetailsId { get; set; }
public virtual ProductMaster ProductMaster { get; set; }
}
}
If we have two model classes then how to replace below single Model namespace ?
#model IEnumerable
I cannot create a single class as below, because my model has two classes,
public Class ProductCustomModel
{
public int ProductId { get; set; }
public int ProductName { get; set; }
public int Price { get; set; }
public int ManufactureBy { get; set; }
}
Here is my Model1 Class.
namespace WebApplication1.DataContext
{
using System;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Data.Entity.Spatial;
public partial class Model1 : DbContext
{
public Model1()
: base("name=Model1")
{
}
public virtual DbSet<ProductDetail> ProductDetails { get; set; }
public virtual DbSet<ProductMaster> ProductMasters { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ProductDetail>()
.Property(e => e.ManufactureBy)
.IsUnicode(false);
modelBuilder.Entity<ProductMaster>()
.Property(e => e.ProductName)
.IsUnicode(false);
}
}
}
If you getdata in varResult than you should try,
In Conroller.
Model1 db=new Model1();
var varResult= (from pm in db.ProductMasters join pd in db.ProductDetails on pm.ProductId equals pd.ProductId select new { pm.ProductId, pm.ProductName, pd.Price, pd.ManufactureBy }).ToList();
return View("path to the view",varResult);
In View
#model ModelName
#foreach (var result in Model){
}
You should need to change the ViewBag to the customModel/Class which will send the List to your view.
Models:
namespace WebApplication1.DataContext
{
public class Repositary
{
public ProductMaster productMaster { get; set; }
public ProductDetail productDetail { get; set; }
}
public Class ProductCustomModel
{
public int ProductId { get; set; }
public int ProductName { get; set; }
public int Price { get; set; }
public int ManufactureBy { get; set; }
}
}
Change your action Method to this:
public ActionResult ProductMasterWithDetails()
{
Model1 db=new Model1();
Repositary rep = new Repositary();
var varResult= from pm in db.ProductMasters join pd in db.ProductDetails on pm.ProductId equals pd.ProductId select new { pm.ProductId, pm.ProductName, pd.Price, pd.ManufactureBy };
//Make your List Here like this and add the values in it.
List<ProductCustomModel> productCustomModelList = new List<ProductCustomModel>();
Foreach(ProductCustomModel item in varResult)
{
ProductCustomModel singleData = new ProductCustomModel();
ProductId = item.ProductId,
ProductName = item.ProductName,
Price = item.Price,
ManufactureBy = item.ManufactureBy
productCustomModelList.Add(singleData);
}
return View(productCustomModelList);
}
In the View(.cshtml)
#model IEnumerable<WebApplication1.DataContext.ProductCustomModel>
#foreach (var result in Model){
//.. operations
}

LINQ EF7 ASP.NET and many-to-many

How i could add records to my database , with relation many-to-many in ASP.NET MVC 6 ?
I tried like that, but it's wrong
[HttpPost]
public IActionResult addpersons(Person pers)
{
var ourdoc = db.Documents.FirstOrDefault(p => p.Documentid == docselected);
ourdoc.DocPers.Add(ourdoc);
db.SaveChanges();
return Content("s");
}
Listings of my model files :
DocContext.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Data.Entity;
namespace TDS_1_ASPNETMVC6.Models
{
public class DocPer
{
public int Documentid { get; set; }
public Document Document { get; set; }
public int Personid { get; set; }
public Person Person { get; set; }
}
public class DocContext : DbContext
{
public virtual DbSet<Document> Documents { get; set; }
public virtual DbSet<Person> Persons { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DocPer>()
.HasKey(t => new
{
t.Documentid,
t.Personid
});
modelBuilder.Entity<DocPer>()
.HasOne(pt => pt.Document)
.WithMany(p => p.DocPers)
.HasForeignKey(pt => pt.Documentid);
modelBuilder.Entity<DocPer>()
.HasOne(pt => pt.Person)
.WithMany(t => t.DocPers)
.HasForeignKey(pt => pt.Personid);
}
}
}
Documents.cs
using System.Collections.Generic;
namespace TDS_1_ASPNETMVC6.Models
{
public class Document
{
public int Documentid { get; set; }
public string Name { get; set; }
public string Props { get; set; }
public List<DocPer> DocPers { get; set; }
}
}
Person.cs
using System.Collections.Generic;
namespace TDS_1_ASPNETMVC6.Models
{
public class Person
{
public int Personid { get; set; }
public string Iname { get; set; }
public string Fname { get; set; }
public string Oname { get; set; }
public string Email { get; set; }
public List<DocPer> DocPers { get; set; }
}
}
First, your controller is not checking for null; what if var ourdoc = db.Documents.FirstOrDefault(p => p.Documentid == docselected); is null? So check for null first :)
Also ourdoc.DocPers.Add(ourdoc); must be changed to ourdoc.DocPers.Add(new DocPer {Person = person});
Second, to create a many-to-many I suggest you to remove the DocPer table and map it "behind", like this:
public class Document
{
public Document()
{
Persons = new List<Person>();
}
public int Documentid { get; set; }
public string Name { get; set; }
public string Props { get; set; }
public virtual ICollection<Person> Persons { get; set; }
}
public class Person
{
public Person()
{
Documents = new List<Document>();
}
public int Personid { get; set; }
public string Iname { get; set; }
public string Fname { get; set; }
public string Oname { get; set; }
public string Email { get; set; }
public virtual ICollection<Document> Documents { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Document>()
.HasMany<Person>(s => s.Persons)
.WithMany(c => c.Documents)
.Map(cs =>
{
cs.MapLeftKey("Documentid");
cs.MapRightKey("Personid");
cs.ToTable("DocPer");
});
}
DocPer table will be managed by the EF without exposing it. Check this site for more details. This guide will apply to EF6 only, not EF7 (which does not support many-to-many yet, for that support check this link). BTW, EF7 is still is not stable yet, so is possibile that this support will be offered in the future, but no one knows yet.

Cant Add controller which uses Class which is inherited from other class

I'm using Entity Framework and MVC3, and my problem is that I can't scaffold Controllers if the class inherits from another Class.
Example:
This is Base Class
using System;
using System.Collections.Generic;
namespace CRMEntities
{
public partial class Company
{
public int Id { get; set; }
}
}
This is Lead Class (Child)
using System;
using System.Collections.Generic;
namespace CRMEntities
{
public partial class Lead : Company
{
public Lead()
{
this.Status = 1;
this.IsQualified = false;
}
public Nullable<short> Status { get; set; }
public Nullable<bool> IsQualified { get; set; }
}
}
When I tried to add controller below error comes...
Context Class COde
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
namespace CRMEntities
{
public partial class CRMWebContainer : DbContext
{
public CRMWebContainer()
: base("name=CRMWebContainer")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Contact> Contacts { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Location> Locations { get; set; }
public DbSet<Task> Tasks { get; set; }
public DbSet<EventInfo> EventInfoes { get; set; }
public DbSet<Opportunity> Opportunities { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<Document> Documents { get; set; }
public DbSet<LoginInformation> LoginInformations { get; set; }
public DbSet<CRMLog> CRMLogs { get; set; }
public DbSet<EntitySharing> EntitySharings { get; set; }
public DbSet<EntityFlagging> EntityFlaggings { get; set; }
public DbSet<EntityTagging> EntityTaggings { get; set; }
public DbSet<EntitySubscribing> EntitySubscribings { get; set; }
public DbSet<Compapny> Compapnies { get; set; }
}
}
The MVC AddController window check for a property DbSet of the ModelType you are adding.
You should do like vicentedealencar said, add to your CRMWebContainer:
[Obsolete("Design only", true)]
public DbSet<Lead> Leads { get; set; }
Remember that u should not use this property in your code (this is why the Obsolete Attribute), since the right way to get the Leads is using:
var leads = Companies.OfType< Lead >();

Define the key for this EntityType

I get this error on this line of code -
ReportRunnerEntities reportDB = new ReportRunnerEntities();
public ActionResult Index()
{
**var types = reportDB.ReportTypes.ToList();**
return View(types);
}
The tables in the databse have primary keys defined and identities set.
My models are -
namespace ReportRunner.Models
{
public partial class ReportRunnerEntities : DbContext
{
public DbSet<Reports> Report { get; set; }
public DbSet<ReportTypes> ReportTypes { get; set; }
public DbSet<Users> Users { get; set; }
}
}
namespace ReportRunner.Models
{
public partial class ReportTypes
{
public int ReportTypeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Reports> Reports { get; set; }
}
}
namespace ReportRunner.Models
{
public class Reports
{
public int ReportId { get; set; }
public int ReportTypeId { get; set; }
public int UserId { get; set; }
public string Title { get; set; }
public ReportTypes ReportType { get; set; }
}
}
namespace ReportRunner.Models
{
public partial class Users
{
public int UserId { get; set; } //ArtistId
public string Name { get; set; }
}
}
and here is my connection string -
I suspect that it's never reaching the database. As I said the keys are set in the database.
Am I missing something?
There are a couple things I see that should change:
ReportTypes should be ReportType
public List Reports { get;
set; } should be public
ICollection Reports { get;
set; }
If you are defining a
connection string in your web.config,
you need to tell EF what one it is
using the constructor in your
ReportRunnerEntities class like this:
namespace ReportRunner.Models
{
public partial class ReportRunnerEntities : DbContext
{
public ReportRunnerEntities : base("name=NameOfConnectionInWebConfig")
{}
public DbSet<Reports> Report { get; set; }
public DbSet<ReportTypes> ReportTypes { get; set; }
public DbSet<Users> Users { get; set; }
}
}
You can read more on that here : http://blogs.msdn.com/b/adonet/archive/2011/01/27/using-dbcontext-in-ef-feature-ctp5-part-2-connections-and-models.aspx
Just on a side note, if you are planning on using .NET MVC and EF Code First as your stack, I would start using the Repository and Unit of Work pattern. Here is a good post on how to set that up: Entity Framework 4 CTP 4 / CTP 5 Generic Repository Pattern and Unit Testable

Resources