ASP.NET MVC 3 EF Code First - How do I make a model that optionally refers to a parent of its own type? - asp.net-mvc

I'm trying to create a model that can optionally refer to a parent of the same type, for example:
public class Category
{
public virtual long CategoryID { get; set; }
public virtual Category? ParentCategory { get; set; }
public virtual int UserID { get; set; }
public virtual string Name { get; set; }
}
As you can see there is an optional member called ParentCategory that is optional and refers to a class of type Category (i.e. the same type). As I'm sure you can guess, I'm trying to create a simple Category tree, where the root node(s) will not have a parent.
This results in the following error when the Entity Framework tries to create the database:
"The ForeignKeyAttribute on property 'ParentCategoryID' on type 'MyProject.Models.Category' is not valid. The navigation property 'Category' was not found on the dependent type 'MyProject.Models.Category'. The Name value should be a valid navigation property name."
I also tried this:
public class Category
{
public virtual long CategoryID { get; set; }
[ForeignKey("Category")]
public virtual long? ParentCategoryID { get; set; }
public virtual int UserID { get; set; }
public virtual string Name { get; set; }
}
But again this resulted in the same error.
Is it possible to model this using EF Code First? Its easy to model it int he database if I were to create the database manually.
Thanks in advance
Ben

Your first example wouldn't even compile because T?, a shortcut for Nullable<T> can only be applied to value types.
The following works fine here:
public class Category
{
public virtual long CategoryID { get; set; }
public virtual Category ParentCategory { get; set; }
}
Now, this will use an ugly name by default for the FK, ParentCategory_CategoryID.
This is a way to get a nicer name, plus some flexibility when using it:
public class Category
{
public virtual long CategoryID { get; set; }
[ForeignKey("ParentCategoryID")]
public virtual Category ParentCategory { get; set; }
public virtual long? ParentCategoryID { get; set; }
}

Related

ASP.NET MVC 5 Model Navigation Properties Error

So I have two models that I am trying to get working,
public class Contract
{
[Key]
public int Id { get; set; }
public string AwardId { get; set; }
[ForeignKey("Id")]
public virtual ICollection<ContractHistory> contractHistory { get; set; }
}
And
public class ContractHistory
{
[Key]
public int Id { get; set; }
public string AwardId { get; set; }
[ForeignKey("Id")]
public virtual Contract contract { get; set; }
}
There are many ContractHistory related to one Contract.
I keep getting a run-time InvalidCastException with that setup but when I remove the navigation property within the ContractHistory, and modify the Contract navigation property to:
public virtual ContractHistory contractHistory { get; set; }
It performs the join correctly without any errors.
I only have a year of experience working with .NET and at a lose as to what the issue could be. Ideally, I would like to navigate from Contract to a list of ContractHistory and from ContractHistory to one Contract. Worst case would be for me to create independent data repository methods to explicitly pull in these records but would prefer to figure out the solution to this problem for future reference.
Any advice or follow up questions are welcomed and thanks in advance!
Your ContractHistory class does not have a foreign key property.
Furthermore, you don't need to have the ForeignKeyAttribute overtop of public virtual ICollection<ContractHistory> contractHistory { get; set; } because you should be referencing the foreign key in the ContractHistory class.
The ForeignKeyAttribute constructor takes a string as a parameter.
Per MSDN
Parameters
name
Type: System.String
If you add the ForeigKey attribute to a foreign key property, you should specify the name of the associated navigation property. If you add the ForeigKey attribute to a navigation property, you should specify the name of the associated foreign key(s). If a navigation property has multiple foreign keys, use comma to separate the list of foreign key names.
So in your case:
[ForeignKey("Id")]
public virtual Contract contract { get; set; }
You are placing the ForeignKeyAttribute on the navigation property, contract. So essentially your class will search for a property called Id and identify that as the foreign key. But in your class, Id has the Key attribute which means you are declaring that as your primary key.
So in order to resolve this, you need to add another property to your ContractHistory class that will be the foreign key. And then change the string in your ForeignKeyAttribute to the name of that property.
Example:
public class ContractHistory
{
[Key]
public int Id { get; set; }
public string AwardId { get; set; }
public int contractID {get;set;}
[ForeignKey("contractID")]
public virtual Contract contract { get; set; }
}
Hope this helps!
As far as I see you want to implement one-to-many relationship, if so you have to remove the foreginkey attribute from the contract history collection in the contract class, and add contractId property as foreignkey in the contract history class as below:
public class Contract {
[Key]
public int Id { get; set; }
public string AwardId { get; set; }
public virtual ICollection<ContractHistory> contractHistory { get; set; }
}
public class ContractHistory {
[Key]
public int Id { get; set; }
public string AwardId { get; set; }
public int ContractId { get; set;}
[ForeignKey("ContractId")]
public virtual Contract contract { get; set; }
}

Error with relationship MVC

Receiving this error when trying to create a controller. Happens right after I choose create.
You must specify the ForeignKey attribute as follows:
public virtual Ingredient Ingredients { get; set; }
[ForeignKey("Ingredients")]
public int IngredientId { get; set; }
Basically, you must specify the name of the property in the ForeignKey attribute.
Anyway, you can also write the opposite:
public int IngredientId { get; set; }
[ForeignKey("IngredientId")]
public virtual Ingredient Ingredients { get; set; }
See more info here

When to create a navigation property in model

As a new programmer I am trying to understand when I should create navigation properties for my Models. What are the trade offs?
On the extremes, the model can just contain ever field need, or the base model can have navigation properties for every field?
Or should navigation property only be used for complex related entities (an entity with more than one property)?
Another consideration I am considering is what works best for MVC drop down or jquery autocomplete. How will designing the model impact application and sql performance?
public class Vendor
{
public int ID { get; set; }
public string Name { get; set; }
public string Number { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
}
or I could do this and assume the related tables also exists in the namespace and in turn the database.
public class Vendor
{
public int ID { get; set; }
public virtual Name Name { get; set; }
public virtual Number Number { get; set; }
public virtual Street Street { get; set; }
public virtual City City { get; set; }
public virtual State State { get; set; }
public virtual PostalCode PostalCode { get; set; }
public virtual Country Country { get; set; }
}
I can see that some of the related entities will be static in nature like States but Number and Street will grow over time.
If your property can be represented by a simple database column, then you use the first approach, if you property itself has its own properties, then you will need a navigational property. If your case, if the City also has properties such as Id, Name, etc, then you will use the navigation property, however, if you only want to store city as the name, your first approach will work.

asp.net MVC 4 EntityType: EntitySet has no keys defined

I am a MVC newbie so go easy on me please.
I am getting two errors when I try to add a migration. They are as follows:
EntityType 'Icon' has no key defined. Define the key for this EntityType.
EntityType: EntitySet 'Icons' is based on type 'Icon' that has no keys defined.
I am including the Icon inside another model, like so:
public class Icon
{
public string IconName { get; set; }
public string IconColor { get; set; }
public int BackgroundXPos { get; set; }
public int BackgroundYPos { get; set; }
public string IconColorHover { get; set; }
public int BackgroundHoverXPos { get; set; }
public int BackgroundHoverYPos { get; set; }
}
public class GalleryThumbnail : CSSBoxModel
{
[DisplayName("Thumbnail Image Outline Color")]
public string ThumbnailImageOutlineColor { get; set; }
[DisplayName("Thumbnail Menu Font")]
public CSSFont ThumbnailMenuFont { get; set; }
[DisplayName("Thumbnail Icon Color")]
public Icon ThumbnailIconColor { get; set; }
}
How is this Address class below any different which is working:
public class Address
{
public String Adress1 { get; set; }
public String Adress2 { get; set; }
public String Adress3 { get; set; }
public String City { get; set; }
public String County { get; set; }
public String State { get; set; }
public String Zip { get; set; }
public String Country { get; set; }
}
[Table("UserProfile")] //Could be PP empolyee, Subscriber or Subscriber's customer
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public bool? Gender { get; set; }
public Address Address { get; set; } //billing address
public Address ShipAddress { get; set; }
}
I did not add a key in either my Icon or Address class because I have no intention of storing specific data in my DB. They are merely to be used inside other classes. So wy is one neededing an ID and the other is not?
I have not created public DbSet Icons { get; set; } in my DB Context either.
Also can you tell me what it is called when you use a class inside another ( or instance of class inside a class as in these examples ) ?
Much appreciated!
Since the address entity has no key defined it the Entity Framework assumes it's a complex property, and your UserProfile table will be rendered with columns named Addres_Address1, Address_Address2, Address_Address3, Address_City, and so on...
Even though you haven't declared an EntitySetIcons DbSet on your context class, it's still being added implicitly because one of your other classes somewhere has an ICollection or IEnumerable property defined.
More info on Code Conventions here:
http://msdn.microsoft.com/en-us/data/jj679962.aspx
So, either decorate the collections as NotMapped like #Kamyar said or simply remove the references from any class already declared as a DbSet.
you can use [NotMapped] attribute in System.ComponentModel.DataAnnotations.Schema namespace in EntityFramework.dll:
using System.ComponentModel.DataAnnotations.Schema;
...
[NotMapped]
public Address Address { get; set; } //billing address
[NotMapped]
public Address ShipAddress { get; set; }
Regarding the naming, AFAIK these are called public properties as well.

EF4 CTP5 - Map foreign key without object references?

I feel like this should have a simple answer, but I can't find it.
I have 2 POCOs:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Product
{
public int Id { get; set; }
public int CategoryId { get; set; }
}
Notice that there are no object references on either POCO. With Code-First, how do I make EF4 CTP5 define a relationship between the two database tables?
(I know this is an unusual scenario, but I am exploring what's possible and what's not with Code-First)
No, this is not possible. As you can see below, all of the fluent API methods for setting up associations require specifying the Navigation Property as their parameter.
HasMany<TTargetEntity>(Expression<Func<TEntityType, ICollection<TTargetEntity>>> navigationPropertyExpression)
HasOptional<TTargetEntity>(Expression<Func<TEntityType, TTargetEntity>> navigationPropertyExpression)
HasRequired<TTargetEntity>(Expression<Func<TEntityType, TTargetEntity>> navigationPropertyExpression)
Is there any particular reason you don't want to use object references? It looks very elegant to use them:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
public int Id { get; set; }
public Category Category { get; set; }
}
And you can still access the Category Id via your product as product.Category.Id.

Resources