Entity Framework 5 Code First Relationships - c#

I'm having trouble understanding how to create relationships between classes on a project that I'm building.
I have a class Photo that has a required one-to-one relationship with PhotoExif, and Photo has an optional one-to-one relationship with FeaturedPhoto.
I'm getting the error:
Unable to determine composite primary key ordering for type Website.Models.PhotoExif. Use the ColumnAttribute or the HasKey method to specify an order for composite primary keys.
Help would be much appreciated.
Photo.cs
public class Photo
{
[Key]
public int PhotoID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public Orientation Orientation { get; set; }
public int Rating { get; set; }
public string URL { get; set; }
public string Filename { get; set; }
public DateTime DateAdded { get; set; }
public bool Hide { get; set; }
public string MetaDescription { get; set; }
public string MetaKeywords { get; set; }
public virtual PhotoExif PhotoExif { get; set; }
}
PhotoExif.cs
public class PhotoExif
{
[Key]
public int PhotoExifID { get; set; }
public int PhotoID { get; set; }
public string ShutterSpeed { get; set; }
public string Aperture { get; set; }
public string FocalLength { get; set; }
public int ISO { get; set; }
public string ExposureBias { get; set; }
public bool Flash { get; set; }
public string WhiteBalance { get; set; }
public string Lens { get; set; }
public DateTime DateTaken { get; set; }
public float Longitude { get; set; }
public float Latitude { get; set; }
public int Zoom { get; set; }
public string Location { get; set; }
public virtual Photo Photo { get; set; }
}
FeaturedPhoto.cs
public class FeaturedPhoto
{
[Key]
public int FeaturedPhotoID { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string InformationLocation { get; set; }
public string ImagePosition { get; set; }
public virtual Photo Photo { get; set; }
}

As per the error message:
Use the ColumnAttribute or the HasKey method to specify an order for
composite primary keys.
you need to add [Column(Order="#")] annotations to PhotoID and PhotoExifID properties of the PhotoExif table.

To me it looks that you don't want a composite primary key on PhotoExif. I don't know why EF tries to infer a composite key, but the reason is possibly 1) that the Photo property has the PhotoID property as foreign key by convention, 2) in a one-to-one relationship the foreign key must be identical with the primary key, 3) there is another property PhotoExifID you have marked a key. So, maybe, EF assumes that this marked key plus the infered key from the one-to-one relationship form a composite key together. (This behaviour would be pretty strange, but I can't see how your model and your annotations could lead to this exception about composite key ordering.)
Anyway, the PhotoID property doesn't seem right, because in a one-to-one relationship principal and dependent must share the same primary key and the FK of the dependent is the PK at the same time. I would try to remove this property and add a FK attribute:
public class PhotoExif
{
[Key]
public int PhotoExifID { get; set; }
public string ShutterSpeed { get; set; }
//...
[ForeignKey("PhotoExifID")]
public virtual Photo Photo { get; set; }
}
Similarly you must define the FK for FeaturedPhoto, otherwise EF cannot determine what's the principal and what's the dependent of the relationship. Depending on the details of the relationships - are they required-required, required-optional or optional-optional and which entity is principal and which one is dependent? - it might be necessary to define the mapping with Fluent API since data annotations do not support every mapping option that Fluent API does.

Related

Entity framework Code First One-to-One relationship

I have two entities which I want to be connected 1:1 relationship. User is principal and UserActivation is dependent, but I have no idea how that works.
public class User
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public string Lastname { get; set; }
public string Username { get; set; }
public virtual UserActivation UserActivation { get; set; }
}
public class UserActivation
{
[Key]
public Guid Id { get; set; }
public Guid UserId { get; set; }
public bool Active { get; set; }
public virtual User User { get; set; }
}
I have tried to remove 'virtual' keyword, have tried to add ForeignKey("UserId") or ForeignKey("User"), I've even tried to make [Key, ForeignKey("User") and none of them helped me. I want to make 1:1 relationship using only dataannotations. Any help is really appreciated. Also my both classes has their own PKs.
Foreign keys are not supported for 1:1 try:
public class User
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public string Lastname { get; set; }
public string Username { get; set; }
public virtual UserActivation UserActivation { get; set; }
}
public class UserActivation
{
[Key]
[ForeignKey("User")]
public Guid Id { get; set; }
public bool Active { get; set; }
public virtual User User { get; set; }
}
Unable to determine the principal end of an association between the types ‘Model.PersonPhoto’ and ‘Model.Person’. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
Julie Lehrman discusses this in her Code First book:
"This problem is most easily solved by using a ForeignKey annotation
on the dependent class to identify that it contains the foreign key.
When configuring one-to-one relationships, Entity Framework requires
that the primary key of the dependent also be the foreign key. In our
case PersonPhoto is the dependent and its key, PersonPhoto.PersonId,
should also be the foreign key. Go ahead and add in the ForeignKey
annotation to the PersonPhoto.PersonId property, as shown in Example
4-21. Remember to specify the navigation property for the relationship
when adding the ForeignKey annotation."
This post is quite old so I thought I'd post the EF 6 solution
Try this...
public class User
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public string Lastname { get; set; }
public string Username { get; set; }
public virtual UserActivation UserActivation { get; set; }
}
public class UserActivation
{
[ForeignKey("User")]
public Guid Id { get; set; }
public Guid UserId { get; set; }
public bool Active { get; set; }
public virtual User User { get; set; }
}

Code First Foreign Keys between two entities

OK what am I missing here or is this just able to be done with data annotation?
I have a Document Entity Model which has a Foreign Key to a User that added the document (one-to-one relationship):
[Table("Documents", Schema = "Configuration")]
public class Document : IPrimaryKey {
[Key]
public long Id { get; set; }
[Required]
public string OrginalName { get; set; }
[Required]
public DocumentTypes DocumentType { get; set; }
[Required]
public MIMETypes MIMEType { get; set; }
[Required]
public byte[] Data { get; set; }
[DefaultValue(false)]
public bool IsPublic { get; set; }
[Required]
public DateTimeOffset DateTimeAdded { get; set; }
[Required]
public long AddedByUser { get; set; }
[ForeignKey("AddedByUser")]
public virtual Details Details { get; set; }
}
I then have a User (Details) Entity that can have an image file (which is stored in the document entities model (none|one-to-one relationship):
[Table("Details", Schema = "User")]
public class Details : IPrimaryKey {
[Key]
public long Id { get; set; }
[Required]
public string UserId { get; set; }
[ForeignKey("UserId")]
public AppUser User { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[CollectionRequired(MinimumCollectionCount = 1)]
public ICollection<Address> Addresses { get; set; }
[CollectionRequired(MinimumCollectionCount = 1)]
public ICollection<Email> Emails { get; set; }
[CollectionRequired(MinimumCollectionCount = 1)]
public ICollection<PhoneNumber> PhoneNumbers { get; set; }
public ICollection<NotificationHistory> NotificationHistory { get; set; }
public long TimeZoneId { get; set; }
public long? ImageId { get; set; }
[ForeignKey("ImageId")]
public virtual Document Document { get; set; }
[ForeignKey("TimeZoneId")]
public virtual TimeZone TimeZone { get; set; }
}
When I try to create a Migration I get this error:
Unable to determine the principal end of an association between the
types 'StACS.PeoplesVoice.DataAccessLayer.EntityModels.User.Details'
and
'StACS.PeoplesVoice.DataAccessLayer.EntityModels.Configuration.Document'.
The principal end of this association must be explicitly configured
using either the relationship fluent API or data annotations.
UPDATED:
While still researching this I made two changes and was able to get around the error but this created an unexpected result in my database.
In the Document Entity I added:
public virtual ICollection<Details> Details { get; set; }
In the Details (user) Entity I added:
puflic virtual ICollection<Document> Documents { get; set; }
In my DB Tables I now have the foreign key on the field I want but I have a secondary foreign key for each respectively.
I tried just removing the single virtual reference and left ONLY the ICollection Virtual reference, now I have no foreign key at all.
UPDATED (based on Akash Kava Suggestion):
I have made the following changes
[Table("Documents", Schema = "Configuration")]
public class Document : IPrimaryKey {
[Required]
public string OrginalName { get; set; }
[Required]
public DocumentTypes DocumentType { get; set; }
[Required]
public MIMETypes MIMEType { get; set; }
[Required]
public byte[] DocumentData { get; set; }
[DefaultValue(false)]
public bool IsPublic { get; set; }
[Required]
public DateTimeOffset DateTimeAdded { get; set; }
[Required]
public long AddedByUser { get; set; }
[Key]
public long Id { get; set; }
[ForeignKey("AddedByUser")]
[InverseProperty("Image")]
public virtual Details User { get; set; }
}
[Table("Details", Schema = "User")]
public class Details : IPrimaryKey {
[Required]
public string UserId { get; set; }
[ForeignKey("UserId")]
public AppUser User { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[CollectionRequired(MinimumCollectionCount = 1)]
public ICollection<Address> Addresses { get; set; }
[CollectionRequired(MinimumCollectionCount = 1)]
public ICollection<Email> Emails { get; set; }
[CollectionRequired(MinimumCollectionCount = 1)]
public ICollection<PhoneNumber> PhoneNumbers { get; set; }
public ICollection<NotificationHistory> NotificationHistory { get; set; }
public long TimeZoneId { get; set; }
public long? ImageId { get; set; }
[ForeignKey("ImageId")]
[InverseProperty("User")]
public Document Image { get; set; }
[ForeignKey("TimeZoneId")]
public virtual TimeZone TimeZone { get; set; }
[Key]
public long Id { get; set; }
}
I have commented out the Fluent API Code
Unable to determine the principal end of an association between the
types 'StACS.PeoplesVoice.DataAccessLayer.EntityModels.User.Details'
and
'StACS.PeoplesVoice.DataAccessLayer.EntityModels.Configuration.Document'.
The principal end of this association must be explicitly configured
using either the relationship fluent API or data annotations.
You can achieve same with Data Annotation as well, you are missing InverseProperty attribute, which resolves ambiguity in this case. Conceptually, every navigation property has Inverse Navigation property, EF automatically detects and assumes inverse property based on type, but if two entities are related to each other by multiple FK properties, you have to explicitly specify InverseProperty attribute on corresponding navigation properties.
I would recommend putting InverseProperty on every navigation property, which helps reduce startup time for EF as EF does not have to determine and validate the model.
Example,
public class AccountEmail {
public long AccountID {get;set;}
// Inverse property inside Account class
// which corresponds to other end of this
// relation
[InverseProperty("AccountEmails")]
[ForeignKey("AccountID")]
public Account Account {get;set;}
}
public class Account{
// Inverse property inside AccountEmail class
// which corresponds to other end of this
// relation
[InverseProperty("Account")]
public ICollection<AccountEmail> AccountEmails {get;set;}
}
I have written a text template which generates all these navigation properties based on current schema. Download all three files from https://github.com/neurospeech/atoms-mvc.net/tree/master/db-context-tt, you might have to customize this as it adds few more things based on our framework, but it does generate pure code model from your database directly.
OK I finally figured this out. Sadly this is not very straight forward as I think Data Annotation should work BUT it does not.
You HAVE to use Fluent API:
modelBuilder.Entity<Details>()
.HasOptional(x => x.Document)
.WithMany()
.HasForeignKey(x => x.ImageId);
modelBuilder.Entity<Document>()
.HasRequired(x => x.User)
.WithMany()
.HasForeignKey(x => x.AddedByUser);

How do I establish a one-to-one relationship using Database First?

When my models were generated, many relationships were mapped automagically. However, some of the relationships are "incorrect" (or at least, not what I want), or missing.
I don't doubt that this is because of poor database design, but based on my role in this project there is not much I can do to fix that. However, is there something I can do in my application code to fix the mapping?
Here is one example:
I would like to map the StoreProductId property to the StoreProducts table.
ProductAttributePriceAdjustment
public partial class ProductAttributePriceAdjustment
{
public int AdjustmentId { get; set; }
public int StoreProductId { get; set; }
public int StoreId { get; set; }
public string ProductId { get; set; }
public Nullable<int> ProductSizeId { get; set; }
public Nullable<decimal> Adjustment { get; set; }
public int PointsAdjustment { get; set; }
public Nullable<int> ProductColorID { get; set; }
public StoreProduct StoreProduct { get; set; }
}
StoreProduct
public partial class StoreProduct
{
public int StoreProductID { get; set; }
public int StoreID { get; set; }
public string ProductID { get; set; }
public bool Featured { get; set; }
public bool Clearance { get; set; }
}
In my view, when I try calling something like:
#adjustment.StoreProduct.ProductID
I get this error:
Object reference not set to an instance of an object.
Update 1
I followed Frans' advice and updated my model to this:
public partial class ProductAttributePriceAdjustment
{
public int AdjustmentId { get; set; }
public int StoreProductId { get; set; }
public int StoreId { get; set; }
public string ProductId { get; set; }
public Nullable<int> ProductSizeId { get; set; }
public Nullable<decimal> Adjustment { get; set; }
public int PointsAdjustment { get; set; }
public Nullable<int> ProductColorID { get; set; }
public virtual StoreProduct StoreProduct { get; set; }
}
but am still getting the same error.
You cannot create a 1:1 mapping in entity framework like this. It's not supported.
Entity Framework only supports 1:1 mappings in which both tables have a shared primary key (ie they have the same primary key, and one of them is a foreign key to the other). In your situation, you are actually creating a 1 to many, because there is no guarantee that StoreProductId is unique.

Entity Framework 1:0..1 relation in mvc 5

I wanted to create a relation between two of my models to be 1:0..1 but all i got is a 1..* relation:
public class MedicalExamination
{
public int Id { get; set; }
public DateTime EntryDate { get; set; }
public DateTime ExecutionDate { get; set; }
public DateTime AcceptanceDate { get; set; }
public string Description { get; set; }
public string State { get; set; }
public string Result { get; set; }
public string Comment { get; set; }
public virtual Visit Visit { get; set; }
[Required]
public virtual ExaminationDictionary ExaminationDictionary { get; set; }
}
and my second model:
public class ExaminationDictionary
{
[Key]
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public virtual MedicalExamination MedicalExamination { get; set; }
}
And after i ran it and updated the database i got a relation like this: http://scr.hu/11m6/4eny0
The thing is that i would like it to be 0..1:1 relation. Does anybody know a good solution for this ?
A 1-to-1 or 1-to-0or1 relation in Entity Framework is only possible if both tables share the same primary key.
So for example, your MedicalExamination is presumably the principal entity in the relationship. It has an Id column primary key. Your ExaminationDictionary table needs to have an Id column that is its primary.
You then describe the relationship using Fluent API like so:
modelBuilder.Entity<MedicalExamination>()
.HasOptional(m=>m.ExaminationDictionary)
.WithRequiredPrincipal(d=>d.MedicalExamination);

Relationships in Entity Framework Code First

yesterday I created database in Management Studio and now I want to create it in program using EF Code First.
Here is link to my database: http://s11.postimg.org/6sv6cucgj/1462037_646961388683482_1557326399_n.jpg
And what I did:
public class GameModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreationTime { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public string TotalTime { get; set; }
public DateTime RouteStartTime { get; set; }
public DateTime RouteEndTime { get; set; }
public int MaxPlayersPerTeam { get; set; }
public int CityId { get; set; }
public int CreatorId { get; set; }
[InverseProperty("Id")]
[ForeignKey("CreatorId")]
//public int TeamId { get; set; }
//[ForeignKey("TeamId")]
public virtual UserModel Creator { get; set; }
public virtual CityModel City { get; set; }
//public virtual TeamModel WinnerTeam { get; set; }
}
public class RegionModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<CityModel> Cities { get; set; }
}
public class CityModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int RegionId { get; set; }
public virtual RegionModel Region { get; set; }
public virtual ICollection<UserModel> Users { get; set; }
public virtual ICollection<GameModel> Games { get; set; }
}
public class UserModel
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public DateTime RegistrationDate { get; set; }
public string FacebookId { get; set; }
public int CityId { get; set; }
public virtual CityModel City { get; set; }
public virtual IEnumerable<GameModel> Games { get; set; }
}
For now I wanted to create 4 tables but I have some problems... I want to make CreatorId in GameModel, but it doesn't work... When i wrote UserId instead of CreatorId it was working ( without [InverseProperty("Id")] and [ForeignKey("CreatorId")]).
This is what i get:
The view 'The property 'Id' cannot be configured as a navigation property. The property must be a valid entity type and the property should have a non-abstract getter and setter. For collection properties the type must implement ICollection where T is a valid entity type.' or its master was not found or no view engine supports the searched locations.
edit:
I changed it like this:
public int CityId { get; set; }
public int CreatorId { get; set; }
[ForeignKey("CityId")]
public virtual CityModel City { get; set; }
[ForeignKey("CreatorId")]
public virtual UserModel Creator { get; set; }
And there is another problem.
The view 'Introducing FOREIGN KEY constraint 'FK_dbo.UserModels_dbo.CityModels_CityId' on table 'UserModels' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.' or its master was not found or no view engine supports the searched locations.
And I have no idea how to solve it.
The InversePropertyAttribute specifies, which navigation property should be used for that relation.
A navigation property must be of an entity type (the types declared in your model, GameModel for example) or some type implementing ICollection<T>, where T has to be an entity type. UserModel.Id is an int, which clearly doesn't satisfy that condition.
So, the inverse property of GameModel.Creator could be UserModel.Games if you changed the type to ICollection<GameModel>, or had to be left unspecified. If you don't specify an inverse property, EF will try to work everything out on its own (in this case it would properly recognize GameModel.Creator as a navigation property, but UserModel.Games would most likely throw an exception, as it is neither an entity type, nor does it implement ICollection<T> with T being an entity type, nor is it a primitive type from a database point of view). However, EF's work-everything-out-by-itself-magic doesn't cope too well with multiple relations between the same entity types, which is when the InversePropertyAttribute is needed.
A quick example that demonstrates the problem:
class SomePrettyImportantStuff {
[Key]
public int ID { get; set; }
public int OtherId1 { get; set; }
public int OtherId2 { get; set; }
[ForeignKey("OtherId1")]
public virtual OtherImportantStuff Nav1 { get; set; }
[ForeignKey("OtherId2")]
public virtual OtherImportantStuff Nav2 { get; set; }
}
class OtherImportantStuff {
[Key]
public int ID { get; set; }
public virtual ICollection<SomePrettyImportantStuff> SoldStuff { get; set; }
public virtual ICollection<SomePrettyImportantStuff> BoughtStuff { get; set; }
}
Here, EF knows that it has to generate 2 FKs from SomePrettyImportantStuff to OtherImportantStuff with the names Id1 and Id2, but it has no way to tell which of the IDs refers to the entity where it was sold from and which is the one it was bought from.
Edit: How to fix the cyclic reference problem
To fix that problem, your context class should override OnModelCreating and configure the foreign keys which shouldn't cascade on delete accordingly, like this:
protected override void OnModelCreating(DbModelBuilder builder)
{
builder.Entity<CityModel>().HasMany(c => c.Users).WithRequired(u => u.City)
.HasForeignKey(u => u.CityId).WillCascadeOnDelete(value: false);
// Add other non-cascading FK declarations here
base.OnModelCreating(builder);
}

Categories

Resources