MVC4 Navigation Properties with composite keys - asp.net-mvc-4

I have created an MVC4 web application using EF-database-first. The tables have composite keys [ID, Name, EffDate], and no foreign keys defined in the database:
For example, Department partial class:
[MetadataType(typeof(DepartmentMetadata))]
public partial class Department
{
public int DeptID { get; set; }
public string DeptName { get; set; }
public System.DateTime EffDate { get; set; }
public string Status { get; set; }
public string RevenueAccount { get; set; }
}
Department metadata class:
public class DepartmentMetadata
{
[Required]
public int DeptID { get; set; }
[Required]
[Display(Name = "Department Name")]
public string DeptName { get; set; }
[Required]
[Display(Name = "Effective Date")]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", NullDisplayText = "--", ConvertEmptyStringToNull = true)]
public System.DateTime EffDate { get; set; }
[Required]
public string Status { get; set; }
[Display(Name = "Revenue Account")]
[StringLength(10)]
public string RevenueAccount { get; set; }
}
The Allocation table, that refers to the Department table. It also has a composite key [DeptID, ProjectID, BillableUnitID, EffDate]. If I could, I would declare the DeptID field a foreign key ...but I don't control the database, and more importantly I believe T-SQL won't allow foreign keys to part of a composite key:
[MetadataType(typeof(AllocationMetadata))]
public partial class Allocation
{
public int DeptID { get; set; }
public int ProjectID { get; set; }
public int BillableUnitID { get; set; }
public System.DateTime EffDate { get; set; }
public string Status { get; set; }
public decimal Allocation1 { get; set; }
}
This works, but I get a column of DeptID numbers. What I would like to have is a column of department names.
A previous question directed me to virtual navigation properties, so I added them:
[MetadataType(typeof(AllocationMetadata))]
public partial class Allocation
{
[ForeignKey("Department")]
public int DeptID { get; set; }
public int ProjectID { get; set; }
public int BillableUnitID { get; set; }
public System.DateTime EffDate { get; set; }
public string Status { get; set; }
public decimal Allocation1 { get; set; }
public virtual Department Department { get; set; } /* navigation property */
}
The code in the AllocationController for Index is:
public ActionResult Index()
{
return View(db.Allocation.Include(a => a.Department).ToList());
}
When I click on the link to Allocation Index view, I get this error message (after I Stop Debugging):
Server Error in '/' Application.
A specified Include path is not valid. The EntityType
'KC_BillableUnit_TESTModel.Allocation' does not declare a navigation
property with the name 'Department'.
Stack trace
[InvalidOperationException: A specified
Include path is not valid. The EntityType
'KC_BillableUnit_TESTModel.Allocation' does not declare a navigation
property with the name 'Department'.]
System.Data.Objects.Internal.ObjectFullSpanRewriter.ConvertSpanPath(SpanPathInfo
parentInfo, List`1 navPropNames, Int32 pos) +8355128
System.Data.Objects.Internal.ObjectFullSpanRewriter..ctor(DbCommandTree
tree, DbExpression toRewrite, Span span) +256
....continues....
I've tried various combinations of annotations, but all result in the same error.
How can I get my Allocation list to show Department names instead of DeptID numbers?

Off course you can! I think the problem is that your declared the navigation property just in one side (Allocation), however you must declare that at both sides (Department too).
The following must resolve your problem:
[MetadataType(typeof(DepartmentMetadata))]
public partial class Department
{
public Department()
{
this.Allocations = new HashSet<Allocation>();
}
// properties ...
public virtual ICollection<Allocation> Allocations { get; set; }
}

Related

Can I reference single foreign key on multiple columns in a table? If yes, how to configure it in Entity Framework core

How can I generate above table like structure using Entity Framework core?
I am using code first approach to generate my tables from domain models which are as follows
public class Contact
{
public int ContactId { get; set; }
public string Email { get; set; }
public string Name { get; set; }
}
public class Company
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
[ForeignKey("Contact")]
public int FirstContact { get; set; }
[ForeignKey("Contact")]
public int SecondContact { get; set; }
[ForeignKey("Contact")]
public int ThirdContact { get; set; }
public virtual Contact Contact { get; set; }
}
In the company table, I want foreign key on columns 'FirstContact', 'SecondContact', 'ThirdContact' which can refer to Contacts table.
I have also tried Fluent API but no success in same also.
Whenever I am running the add-migration command, I am getting this error:
There are multiple properties with the [ForeignKey] attribute pointing to navigation 'Company.Contact'. To define a composite foreign key using data annotations, use the [ForeignKey] attribute on the navigation.
I would appreciate any help regarding the same.
Thanks.
For multiple Navigation Properties, you need multiple Foreign Keys. EG
public class Company
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public virtual Contact FirstContact { get; set; }
public virtual Contact SecondContact { get; set; }
public virtual Contact ThirdContact { get; set; }
}
And let EF Core create shadow properties for your FKs or with Foreign Key properties:
public class Company
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public int FirstCotactId {get; set;}
public virtual Contact FirstContact { get; set; }
public int SecondCotactId {get; set;}
public virtual Contact SecondContact { get; set; }
public int SecondCotactId {get; set;}
public virtual Contact ThirdContact { get; set; }
}

EF CORE 2.0 Incompatible relationship (nullable foreign key)

In our businessslogic we have some contacts row that may have a relationship with one billing row (0..N).
Here the contact class
public class ESUsersContact
{
[Key]
public int Id { get; set; }
[ForeignKey("Billing")]
public int? BillingId { get; set; }
public string Title { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Email { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
public string Phone { get; set; }
public string CellPhone { get; set; }
public string Fax { get; set; }
public ES.Enums.BusinessESCommon.Language.AllLanguage Language { get; set; }
public virtual ICollection<ESUsersCompany> Companies { get; set; }
public virtual ESUsersBilling Billing { get; set; }
}
Here is the billing class
[Key, Column(Order = 0)]
[ForeignKey("Company")]
public int CompanyId { get; set; }
[Key, Column(Order = 1)]
public ES.Enums.BusinessESUsers.Billing.Type Type { get; set; }
public ES.Enums.BusinessESUsers.Billing.Payment PaymentType { get; set; }
public ES.Enums.BusinessESUsers.Billing.Frequency PaymentFrequency { get; set; }
public decimal Amount { get; set; }
public bool IsFreeTrial { get; set; }
public DateTime FreeTrialEndDate { get; set; }
public DateTime? NextPaymentDate { get; set; }
public virtual ESUsersCompany Company { get; set; }
public virtual ICollection<ESUsersContact> Contacts { get; set; }
}
However, I receive this error doing so.
The relationship from 'ESUsersContact.Billing' to 'ESUsersBilling.Contacts' with foreign key properties {'BillingId' : Nullable<int>} cannot target the primary key {'CompanyId' : int, 'Type' : Type} because it is not compatible. Configure a principal key or a set of compatible foreign key properties for this relationship.
I don't understand why the error occurs and state the primary key {'CompanyId' : int, 'Type' : Type}.
ESUsersContact.Billing class has a composite key. ESUsersContact has to map both of the foreign keys. CompanyId and Type. You can not refer only one column since ESUsersContact.Billing has two columns for the key.

Is it possible to create a Domain Class which has Multiple FK Columns to same PK?

I'm a newbie to designing database.
I have problem how to define a domain class which has multiple foreign keys linked with a same primary key.
Here is my model:
namespace OceanFmsSystem.Domain
{
public class ExportTemplate
{
public int Id { get; set; }
public List<ExportBooking> ExportBookings { get; set; }
public string TemplateName { get; set; }
public int CustomerId { get; set; }
public string Incoterms { get; set; }
public string IncotermsDetail { get; set; }
public string PaymentTerm{ get; set; }
public int CountryOriginId { get; set; }
public int CountryDestinationId { get; set; }
}
}
What I want to do is that CountryOriginId & CountryDestinationId should refer to the below class as foreign keys:
namespace OceanFmsSystem.Domain
{
public class Country
{
public int Id { get; set; }
public string CountryCode { get; set; }
public string CountryName { get; set; }
}
}
As far as I know, in EF Core there is an convention which I should name a foreign key as below for migration from code to database.
public type ClassNameOfPrimaryKeyId { get; set;}
Is there any possible way to make this happens?
Yes, possible. Your class should look like this:
public class ExportTemplate
{
//...
public int CountryOriginId { get; set; }
public Country CountryOrigin { get; set; }
public int CountryDestinationId { get; set; }
public Country CountryDestination { get; set; }
}
EF is smart enough to figure the Ids by convention. If you do not wish to follow the convention you can use [ForeignKey] attribute on the properties to configure the FK:
[ForeignKey("Origin")]
public int MyOriginId { get; set; }
public Country Origin { get; set; }

Saving Data using Entity Framework Code First

I am using ASP.NET MVC Razor Entity Framework Code First C#
Class - A
public class Om_Category
{
[Key]
public int CategoryID { get; set; }
public String CategoryName { get; set; }
public String CategorySanitized { get; set; }
public Boolean IsActive { get; set; }
public DateTime CreationDate { get; set; }
}
Class - B
public class Om_CategorySkills
{
[Key]
public Int32 SkillID { get; set; }
public String Skill { get; set; }
public String SkillSanitized { get; set; }
public DateTime CreationDate { get; set; }
public Boolean IsActive { get; set; }
public Om_Category Category { get; set; }
}
When I try to create the record for table Om_CategorySkills. It says
cannot save the duplicate value in Om_Category table.
This is happening because I am sending the Om_Category class object in Om_CategorySkills class object because there are some fields in Om_Category class that are mandatory.
So I am passing the Om_Category class object also in Om_CategorySkills class object. Is there any way to fix this issue ?
Your navigation properties doesn't seem to be right.. Can you try (I didn't test),
public class Om_Category
{
[Key]
public int CategoryID { get; set; }
public String CategoryName { get; set; }
public String CategorySanitized { get; set; }
public Boolean IsActive { get; set; }
public DateTime CreationDate { get; set; }
public virtual Om_CategorySkills CategorySkills{ get; set; }
}
public class Om_CategorySkills
{
[Key]
public Int32 SkillID { get; set; }
public String Skill { get; set; }
public String SkillSanitized { get; set; }
public DateTime CreationDate { get; set; }
public Boolean IsActive { get; set; }
public int CategoryID {get;set;}
public virtual Om_Category Category { get; set; }
}
I see that your Om_CategorySkills object is lacking an Int32 Om_CategoryId property to be used as foreign key. I would also add a virtual modifier to the navigation property Category, in order to allow for lazy loading.
I think that it may be the case that the category object in your new/edited skill is already in the database, but was not the one retrieved by the context, so the context believes you are trying to save a new category with the Id of an existing one.
You should not try to save a skill object with a category object with no changes. Otherwise, the category object should be the one attached to the context.

Joins in Entity Framework 5

I have two classes in my model
[Table("tblPackages")]
public class Packages
{
public int Id { get; set; }
[Required]
[Display(Name = "Package Type")]
public int TypeId { get; set; }
[Display(Name = "No of SMS")]
public int AllowedSMS { get; set; }
[Display(Name = "Time Span in Days")]
public int? TimeSpan { get; set; }
public decimal Price { get; set; }
}
[Table("tblPackageTypes")]
public class PackageTypes
{
public int Id { get; set; }
public string Name { get; set; }
public string Details { get; set; }
public DateTime DueDate { get; set; }
}
and the table is same. now i need to get all from class Packages and just Name from class PackageTypes. How can i do by just using Entity Framework
Add a PackageTypes navigation property to your Packages class and access it by name:
[Table("tblPackages")]
public class Packages
{
public int Id { get; set; }
[Required]
[Display(Name = "Package Type")]
public int PackageTypesId { get; set; }
public PackageTypes PackageTypes { get; get; }
[Display(Name = "No of SMS")]
public int AllowedSMS { get; set; }
[Display(Name = "Time Span in Days")]
public int? TimeSpan { get; set; }
public decimal Price { get; set; }
}
By convention, Entity Framework will match the PackageTypesId and PackageTypes properties based on the naming (although you can use different names if you configure it to do so, but that's a more advanced topic).
Now you can access the Name directly from your Packages objects:
myPackage.PackageTypes.Name
Also, you might want to think about your classes in the singular, not the plural. The class represents a single Package, not the entire collection. Same with the PackageType. It makes your code more understandable:
Package myPackage = new Package();
myPackage.PackageType.Name
Only use plural if your class truly represents the entire collection and not a single item.
Use a navigation property, you will need a FK relationship between Packages and PackageTypes:
[Table("tblPackages")]
public class Packages
{
public int Id { get; set; }
[Required]
[Display(Name = "Package Type")]
public int TypeId { get; set; }
[Display(Name = "No of SMS")]
public int AllowedSMS { get; set; }
[Display(Name = "Time Span in Days")]
public int? TimeSpan { get; set; }
public decimal Price { get; set; }
public int PackageTypesId {get;set;}
public virtual PackageTypes {get;set;}
}
[Table("tblPackageTypes")]
public class PackageTypes
{
public int Id { get; set; }
public string Name { get; set; }
public string Details { get; set; }
public DateTime DueDate { get; set; }
public ICollection<Packages> {get;set;}
}
Like the previous answer states you need a navigation property. From your code I assume TypeId is FK to Id in PackageTypes. If this is so, simply create a property named Type of the type PackageType. When EF finds a navigation property to another entity it tries to find the property with the FK by appending the suffix Id or _Id.
If you on the other want a true composite object joining in fields from several tables you should use a view for this!
Regards
HÃ¥kan