Why is MVC3 not scaffolding my foreign key columns - asp.net-mvc-3

I'm trying to use MVC 3 with EF 4.1 using code first and am following Scott Guthries tutorial http://weblogs.asp.net/scottgu/archive/2011/05/05/ef-code-first-and-data-scaffolding-with-the-asp-net-mvc-3-tools-update.aspx.
The issue I'm having is that when I create the products controller and the related scaffolded views, there is no "category" column being created in any of the views ("edit", "create", "index" etc), which according to the tutorial should be created.
I've traced the reason why the column is not being shown is because of the t4 templates... it is failing a check to see if it is a bindable type in order to display the property as a column.
The logic for checking if it is bindable is:
bool IsBindableType(Type type) {
return type.IsPrimitive || bindableNonPrimitiveTypes.Contains(type);
}
Where bindableNonPrimitiveTypes is a fixed list:
static Type[] bindableNonPrimitiveTypes = new[] {
typeof(string),
typeof(decimal),
typeof(Guid),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
};
I have just installed VS2010 sp1, EF 4.1 and the MVC3 Tools Update referenced by the tutorial.
I'm sure I've followed all the steps...
Where am I going wrong/What am I missing?

I believe that it does work as described in the tutorial - I just went through that tutorial right now and got the expected result (it did scaffold a "Category" column and drop-down list).
My best guess about why it didn't work in your case is that perhaps you missed the CategoryID property from the Product class, or maybe you called it something else. For scaffolding to detect the FK relationship, it's necessary for your entity to have both a "navigation" property (in this case, Category, of type Category) and a "foreign key" property (in this case CategoryID of type int) - without those it won't infer the relationship and hence you wouldn't get the dropdown.
In case it helps, here's the full code for the model classes that you can copy and paste into your project:
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public int CategoryID { get; set; }
public decimal? UnitPrice { get; set; }
public int UnitsInStock { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class StoreContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
Remember to compile your code before using the "Add Controller" window, otherwise it will not realise that you've changed the code.

Related

How can I LINQ select from two EF DbSets and group join - but using AutoMapper

Note: These classes are related, but not part of the same Aggregate (like PurchaseOrder and OrderLine) - so I do not have a navigation property from "One" to "Many".
=== Entities ===
public class One
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Many
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid One { get; set; }
}
=== Contracts ===
public class OneWithMany
{
public Guid Id { get; set; }
public string Name { get; set; }
public IEnumerable<Many>? ManyRelatedObjects { get; set; }
}
I want to select all One objects and any related Many objects from DbSet/DbSet into OneWithMany.
To ensure I don't miss properties added in future I am using ProjectTo in AutoMapper - but I can't work out how to fit it into the equation.
Unfortunately, it seems Entity Framework does not support GroupJoin.
The solution is to do the projection and as much filtering as possible as two separate queries, and then combine them into a result in memory.
If you find EF related answers on the web related to GroupJoin make sure you check the example code to see if they are actually showing code working on arrays instead of DbSet.

Relationship one to many in EF

I have the following problem: it turns out that when I write the controller of my article table from my database, I get an error on my web page. I'm using ASP.NET Core 5 with a pre-existing database. I'm a newbie at this, I'm learning by creating projects.
In my controller class I have this:
https://codeshare.io/3AoPBB
I have this class articlesviewmodel:
https://codeshare.io/mp08vk
And this is my articlemap:
https://codeshare.io/0gQqZv
DbContextSytem: https://codeshare.io/1YD6Bj
Article table in SQL Server database:
https://codeshare.io/DZrPJk
Class Category for a table in SQL Server:
public class Category
{
public int idcategory { get; set; }
[Required]
[StringLength(50, MinimumLength = 3, ErrorMessage = "The Category must not have more than 50 characters")]
public string namecategory { get; set; }
public string descategory { get; set; }
public bool numberstatate { get; set; }
// modify table category
public ICollection<Article> articles { get; set; }
}
I really don't know what I can be doing wrong in the include
I solved it in the following way, it turns out that I got an error because the foreign key was missing between the category and articles tables, I solved it with the following line of code in the dbcontextsystem under modelBuilder.ApplyConfiguration(new ArticleMap());
modelBuilder.Entity<Article>().HasOne(a => a.Category).WithMany(c => c.articles).HasForeignKey(a => a.idcategory);

About MVC ViewModel and partial view [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to edit multiple models in a single Razor View
I'm using MVC3 to make an order functionality. I have Order and OrderDetail Model. Order and OrderDetail is in an one to many relationship.
The two models look something like this:
public class order
{
public int OrderId{get;set;}
public int OrderStatusID { get; set; }
public virtual OrderStatus OrderStatus { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get; set; }
}
public class OrderDetail
{
public int OrderDetailID { get; set; }
public int OrderID { get; set; }
public int Quantity { get; set; }
public string Description { get; set; }
public int CategoryID { get; set; }
public virtual Order Order { get; set; }
public virtual Category Category { get; set; }
}
I want to use them both in a view, so I made the OrderViewModel for it below,
public class OrderViewModel
{
public virtual Order Order { get;set; }
public virtual List<OrderDetail> OrderDetails { get;set; }
}
I have two questions:
1, How can I use the OrderViewModel to make pages for creating and editing Order?
I made the partial view for the OrderDetail. In the Creating page, I did something like the following, but I don't think it's correct.
#foreach (var detail in Model.OrderDetails)
{
#Html.Partial("_OrderDetail", detail)
}
2, Order and OrderDetail is in an one to many relationship, in the creating Order page, I want users to be able to add multiple OrderDetail. I used jQuery to make a button that can make multiple OrderDetail fields, but I don't know how to Retrieve the values from every OrderDetail.
How is this done?
Use EditorFor for the details.
Please have a look of below link and see an answer of Darin Dimitrov
How to use LabelFor on a strongly typed view for a list
LabelFor not working in loop (for/foreach/template)
Do let me know if you still have any query.

How to access an association in view from one POCO with several references to another

Sorry about the title; couldn't think of a better one.
Any way, I'm accessing an associated property in my view like so:
#Model.Company.CompanyName // No problems here...
The model is a viewmodel mapped to an EF POCO. The Model has several properties associated to the Company table. Only one of the properties in the model share the same name as the PK in the Company table. All the other properties reference the same table:
public class MyModelClass
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int AnotherCompanyId { get; set; } // References CompanyId
public int AndAnotherCompanyId { get; set; } // References CompanyId
public Company Company { get; set; }
}
public class Company
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public string Address { get; set; }
}
I'm obviously missing something here.
How can I get the names of the other companies in my Model?
Any help is greatly appreciated.
The model is a viewmodel mapped to an EF POCO
I think you are confusing the notion of a view model. A view model is a class that is specifically designed to meet the requirements of your view. So if in your view you need to display the company name and not the company id then your view model should directly contain a CompanyName property. Or a reference to another view model (CompanyViewModel) which contains the name directly. It is then the responsibility of your controller action to query your domain models (EF entities) and aggregate them into a single view model tat will contain all the necessary information that the view requires.
Here's how a typical view model might look like:
public class MyViewModel
{
public CompanyViewModel Company { get; set; }
public CompanyViewModel AnotherCompany { get; set; }
public CompanyViewModel AndAnotherCompany { get; set; }
}
public class CompanyViewModel
{
public string Name { get; set; }
}
Where the data comes from in this view model is not important. You could have the Company property populated from your EF stuff, the AnotherCompany property populated from a XML file and AndAnotherCompany from WCF.

Can't form some simple POCO's to use with "Code First" Entity Framework, please check for mistake

So I decided to go with the code first/DbContext approach, but already have an existing database file. Nothing complex, so I am thinking I can just create the DbContext derived container class with DbSets for the respective POCO's, create the connection string to my database and I should be set. However I believe I am having difficulties properly declaring the properties in my entity classes since I am getting errors when trying access an object through the navigational properties. Usually telling me Object reference not set to an instance of an object when I try context.Products.Find(1).Category.CATNAME; etc. Also tried declaring the collection properties with virtual keyword to no avail.
Some specifics of the database schema are:
In Categories table the PCATID is a foreign key to the CategoryID in
the same Categories table and can be null.
Both CategoryID and RootCategoryID in Products table can be null and
are both foreign keys to CategoryID in the Categories table.
I am testing things at the moment but will be setting a lot of the fields to non null types eventually.
Here are my entity POCO's and the entity Dbset container class:
public class Category
{
[Key]
public int CategoryID { get; set; }
public string CATNAME { get; set; }
public int PCATID { get; set; }
public ICollection<Category> Categories { get; set; }
public ICollection<Product> Products { get; set; }
}
public class Product
{
[Key]
public int ProductID { get; set; }
public int CategoryID { get; set; }
public int RootCategoryID { get; set; }
public string Name { get; set; }
public string ShortDescription { get; set; }
public string LongDescription { get; set; }
public string Keywords { get; set; }
public decimal ListPrice { get; set; }
public Category Category { get; set; }
}
public class EFDbContext: DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
You need to make PCATID a nullable property as you have said it can be null. Make all those navigation properties and collection properties virtual. EF will not be able to detect the category hierarchy so you have use either attributes or fluent API to configure that.
public class Category
{
[Key]
public int CategoryID { get; set; }
public string CATNAME { get; set; }
[ForeignKey("ParentCategory")]
public int? PCATID { get; set; }
[InverseProperty("Categories")]
public virtual Category ParentCategory { get; set; }
[InverseProperty("ParentCategory")]
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
Requirements for Creating POCO Proxies
Everything looks ready for POCO but Lazy Loading isn't sorted out at this point. By default LL is on, but in order to enable lazy loading, the Category property must be Virtual (a proxy is created that catches the reference and loads the data). If you don't want lazy loading then disable it in your EFDbContext constructor.
So your options are:
public virtual Category Category { get; set; }
or
public class EFDbContext: DbContext
{
public static EFDbContext()
{
LazyLoadingEnabled = false
}
...
}
You'd probably want to do the first one...
Are you certain you really want to use Code First? Or do you just want to use DbContext and DbSet? You can get the same benefits with Database First, using DbContext and DbSet. Since you already have a database, it's generally a lot simpler.
See: http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-model-amp-database-first-walkthrough.aspx
The only difference between Code First and Database First with DbContext is that Code first uses the fluent mapping model, while Database First uses an .edmx file. Maintaining the .edmx is much easier with an existing database.
If you're bound and determined to use Code First, then I suggest getting the Entity Framework Power Tools CTP1 and reverse engineering your database to Code First.
I agree with #Eranga about class Category (+1 to #Eranga).
public class Category {
[Key]
public int CategoryID { get; set; }
public string CATNAME { get; set; }
[ForeignKey("ParentCategory")]
public int? PCATID { get; set; }
[InverseProperty("Categories")]
public virtual Category ParentCategory { get; set; }
[InverseProperty("ParentCategory")]
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
And you also have problem with your Linq query:
context.Products.Find(1).Category.CATNAME;
EF return data only from tables that you request with Include or you use this tables in functions.
With this code all work:
db.Products
.Include(p => p.Category) // here I demand to load data from Category table
.First(p => p.ProductID == 3)
.Category
.CATNAME;

Resources