At start i wanted to mention that i've been fighting this thing for a few days and tried many of the answers more or less related to this problem. Yet I could not resolve it.
I have two classes that represent tables in a DB. These are the existing tables used by legacy application and I cannot change them.
Message can have multiple MessageRecipients.
Environment: MVC3, EF4.1
Classes are:
public class Message
{
[ForeignKey("MessageReciepients")]
public virtual int MessageID { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime Recieved { get; set; }
[ForeignKey("User")]
public int AuthorUserID { get; set; }
//P\\ Navigation properties
public virtual IList<MessageRecipient> MessageReciepients { get; set; }
public virtual User User { get; set; }
}
public class MessageRecipient
{
//[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int MessageID { get; set; }
public int UserID { get; set; }
public bool Read { get; set; }
public bool Important { get; set; }
public bool Deleted { get; set; }
public bool Destroyed { get; set; }
//P\\ Navigation Properties
public virtual User User { get; set; }
}
The error I have is:
The foreign key component 'MessageID' is not a declared property on
type 'MessageRecipient'. Verify that it has not been explicitly
excluded from the model and that it is a valid primitive property.
How to correctly map these classes, relationships to load the recipients of a message?
I can add that the navigation property User works correctly for a Message and loads a User's data correctly.
I'm not too experienced with .NET and I learn while doing this.
I tried some EF API config to map these i tried swearing at it, curse it, and been close to cry and pray at the same time. No Joy!!
I would really appreciate the help.
It turned out that the problem was with the composite key that i needed to use and it all could be solved with some attributes:
This is how it looks now:
public class Message
{
public int MessageID { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime Recieved { get; set; }
[ForeignKey("User")]
public int AuthorUserID { get; set; }
//P\\ Navigation properties
public virtual ICollection<MessageRecipient> MessageRecipients { get; set; }
public virtual User User { get; set; }
}
public class MessageRecipient
{
[Key, Column(Order=0), ForeignKey("User")]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserID { get; set; }
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int MessageID { get; set; }
public bool Read { get; set; }
public bool Important { get; set; }
public bool Deleted { get; set; }
public bool Destroyed { get; set; }
//P\\ Navigation Properties
public virtual User User { get; set; }
}
fill in the missing properties:
public class Message
{
public int MessageID { get; set; }
}
public class MessageRecipient
{
public int MessageID { get; set; }
[ForeignKey("MessageID")]
public Message Message { get; set; }
}
Related
My Entities:
public partial class Student: IBrand
{
public Student()
{
Grades = new HashSet<Grade>();
}
[Key]
public int StudentId { get; set; }
[ForeignKey("Users")]
public string UserId { get; set; }
public ApplicationUser User { get; set; }
[ForeignKey("Parent")]
public int? ParentId { get; set; }
public Parent Parent { get; set; }
public int? SectionId { get; set; }
public string FatherName { get; set; }
public int classNumber { get; set; }
public Section Section { get; set; }
public string brevetResult { get; set; }
public DateTime? dateLeftAec { get; set; }
public string additionalInfo { get; set; }
public bool bacc { get; set; }
public string baccResult { get; set; }
public string baccSection { get; set; }
public int BrandId { get; set; }
[JsonIgnore]
public Brand Brand { get; set; }
public virtual ICollection<StudentRegistration> StudentReg { get; set; }
public virtual ICollection<Grade> Grades { get; set; }
public virtual ICollection<Absence> Absences { get; set; }
public virtual ICollection<StudentStudyYear> StudentStudyYears { get; set; }
}
public class Grade: IBrand
{
public int gradeId { get; set; }
public int grade { get; set; }
public int StudentId { get; set; }
public int SubjectId { get; set; }
public int TeacherId { get; set; }
public int TermId { get; set; }
public int SectionId { get; set; }
public bool IsApproved { get; set; }
public string ResultToEdit { get; set; }
public bool IsEditedByAdmin { get; set; }
public virtual Student Student { get; set; }
public virtual Subject Subject { get; set; }
public virtual Teacher Teacher { get; set; }
public virtual Term Term { get; set; }
public virtual Section Section { get; set; }
public int BrandId { get; set; }
[JsonIgnore]
public Brand Brand { get; set; }
public virtual ICollection<GradeStudyYear> GradeStudyYears { get; set; }
}
I'm trying to get a student with his grades, but I encountered a problem with circular references. I tried to add this to the startup file:
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
That seemed to solve the circular references problem, but then I started getting 5GB worth of data... so that code apparently just suppressed the error without solving the circular data problem.
I tried to put the attribute [JSONIgnore] in my grade.cs file, but I need to get a student from grade so it will not be useful.
How can I solve this circular references problem?
Serializing entities leads to a number of issues and exposes more information about your domain than is needed. When returning data to a view or an API consumer you can instead define a view model or DTO to contain just the details that consumer will need in whatever structure best serves that need. This avoids reference issues which EF navigation properties can cause, reduces the amount of domain knowledge and data you expose to clients, and minimizes the payload size to just what is needed. Data can be flattened, so if you are displaying a list of one entity, you don't need a ViewModel/DTO per related entity, your view model can merely contain relevant details of any related entity that applies to that consumer.
Once you have defined your view model / DTO you can use .Select() to populate it, or set up mapping with Automapper and populate it using .ProjectTo<TViewModel>().
I have an MVC 4 application that is using code first to generate tables and columns in my SQL Server DB. I am trying to figure out how I ended up with an additional TABLE that was not intended. I have looked through some questions but not found the exact same problem I am having. I will try to explain this simply.
I have added a model called Associate which keeps track of associates that my client does business with. Each Associate needs a foriegn key of AssociateTypedID and RegionID.
namespace XXX.Models
{
public class Associate
{
public int AssociateId { get; set; }
public string AssociateName { get; set; }
public int AddressNumber { get; set; }
public string AddressStreet { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zipcode { get; set; }
public string MainPhoneNumber { get; set; }
public string AssociateEmail { get; set; }
public string AssociateWebsite { get; set; }
public string ContactFirstName { get; set; }
public string ContactLastName { get; set; }
public string ContactPhoneNumber { get; set; }
public string ContactEmail { get; set; }
public int RegionId { get; set; }
public int AssociateTypeId { get; set; }
public virtual ICollection<AssociateType> AssociateTypes { get; set; }
public virtual ICollection<Region> Regions { get; set; }
}
}
AND
namespace XXX.Models
{
public class AssociateType
{
public int AssociateTypeId { get; set; }
public string AssociateTypeName { get; set; }
public virtual ICollection<Associate> Associates { get; set; }
}
}
AND
namespace XXX.Models
{
public class Region
{
public int RegionId { get; set; }
public int RegionName { get; set; }
public int RegionDescription { get; set; }
public virtual ICollection<Associate> Associates { get; set; }
}
}
AND
namespace XXX.Models
{
public class XXXDb : DbContext
{
public XXXDb(): base("name=DefaultConnection")
{
}
public DbSet<Associate> Associates { get; set; }
public DbSet<AssociateType> AssociateTypes { get; set; }
public DbSet<Region> Regions { get; set; }
}
}
So I have updated my code above and I'm getting very close to where I need to be in my database. I have the following tables generated.
Associates, AssociateTypes & Regions (each of them have the columns I would expect)
BUT I now have a new table called RegionAssociates which has the following columns:
Region_RegionId (int) & Associate_AssociateId (int)
This table was not expected or needed in my schema.
Your classes doesn't match your description of the model. You are saying
Each Associate can have a designation of AssociateType
I suppose that the same AssociateType can be assigned to more Associates, so there should be 1:N relationship between AssociateType and Associate.
But the Associate class defines the relationship the other way around - by convention public virtual ICollection<AssociateType> AssociateType { get; set; } creates 1:N relationship between Associate and AssociateType.
the correct definition of your classes would be
public class Associate
{
public int AssociateId { get; set; }
public string AssociateName { get; set; }
public int AddressNumber { get; set; }
public string AddressStreet { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zipcode { get; set; }
public string MainPhoneNumber { get; set; }
public string AssociateEmail { get; set; }
public string AssociateWebsite { get; set; }
public int RegionId { get; set; }
public int AssociateTypeId { get; set; }
public virtual AssociateType AssociateType { get; set; }
public string ContactFirstName { get; set; }
public string ContactLastName { get; set; }
public string ContactPhoneNumber { get; set; }
public string ContactEmail { get; set; }
public virtual ICollection<Region> Regions { get; set; }
}
public class AssociateType
{
public int AssociateTypeId { get; set; }
public string AssociateTypeName { get; set; }
public virtual ICollection<Associate> Associates { get; set; }
}
Can't say for sure what is missing from your configuration as you did't post it, but if you are using the fluent api something like this should fix the problem:
modelBuilder.Entity<AssociateType>()
.HasKey(t => t.AssociateTypeId);
modelBuilder.Entity<Associate>()
.HasRequired(t => t.AssociateType)
.WithRequiredPrincipal(t => t.Associate);
The above is adapted from this article http://msdn.microsoft.com/en-us/data/jj591620.aspx
I have the following Classes and am using EF 4.2 Code First
public class PartAttribute
{
public Part Part { get; set; }
public PartAttributeType PartAttributeType { get; set; }
[Timestamp]
public byte[] Time { get; set; }
[Required]
public string Value { get; set; }
}
public class Part
{
public Guid Id { get; set; }
[Required]
public PartType PartType { get; set; }
[Required]
public string SerialNumber { get; set; }
public string Description { get; set; }
public ICollection<Team> SelectedTeams { get; set; }
}
public class PartAttributeType
{
public Guid Id { get; set; }
[Required]
public PartType PartType { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
When I build my model it creates the three tables and the relationships between the tables as I would like/expect them. I am trying to create a composite key on the PartAttribute table between the Part, PartAttributeType, and the time and I can't seem to figure it out. When I try to add
modelBuilder.Entity<PartAttribute>().HasKey(c => new { c.Part, c.PartAttributeType, c.Time });
I get an error saying Part is not a scalar type (which it is not).
You need to introduce foreign key properties which can act as primary keys at the same time:
public class PartAttribute
{
public Guid PartId { get; set; }
public Guid PartAttributeTypeId { get; set; }
public Part Part { get; set; }
public PartAttributeType PartAttributeType { get; set; }
[Timestamp]
public byte[] Time { get; set; }
[Required]
public string Value { get; set; }
}
Then your mapping:
modelBuilder.Entity<PartAttribute>()
.HasKey(c => new { c.PartId, c.PartAttributeTypeId, c.Time });
EF should be able to recognize the new properties as the foreign keys for your two navigation properties due to the naming convention.
I've been a few weeks working on a web project, amd mostly thinking how would I implement data layer. I chosed Entity Framework 4.1, code first model.
So, among lot of other entities , think of PLAYER who has N CHARACTER, that can be in 0..1 GUILD
public class Player
{
public int Id { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public bool IsLogged { get; set; }
public DateTime LastActivity { get; set; }
public DateTime LastLogin { get; set; }
public DateTime LastLogout { get; set; }
public string DisplayName { get; set; }
public string DefaultImage { get; set; }
public virtual Board Board { get; set; }
public virtual PlayerData PlayerData { get; set; }
public virtual ICollection<Character> Characters { get; set; }
}
public class Guild
{
public int Id { get; set; }
public string Name { get; set; }
public string DefaultImage { get; set; }
public virtual ICollection<Character> Characters { get; set; }
}
public class Character
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Player Player { get; set; }
public virtual Guild Guild { get; set; }
public virtual GuildRank GuildRank { get; set; }
public virtual Game Game { get; set; }
}
As you can see, there a lot more entities and relationships, but this will work.
Well it does not, this code:
Character character = mod.Characters.Where(c => c.Player == player).FirstOrDefault();
Throws an exception:
Unable to create a constant value of type 'DataObjects.Player'. Only
primitive types ('such as Int32, String, and Guid') are supported in
this context.
I don't understand why.
I also tried with using [Key] and [ForeingKey] attributes, but I can't find them! :S (though the where in System.Data.Entity.dll, but the don't).
So after so many errors, I started to think maybe I got the whole thing wrong...
Any ideas on how to fix the error, or to go in other direction?
Thanks in advance.
This is stupidity in EF. You cannot compare Player directly. You must compare Ids so your query can be rewritten to:
int playerId = player.Id;
Character character = mod.Characters.FirstOrDefault(c => c.Player.Id == playerId);
I want to created Bi-Directional One-One relationship between two entities using EF Code First. I have trouble with the following code. What do you think I should do?
public class User
{
public string ID { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public int ProfileID { get; set; }
public Profile Profile { get; set; }
}
public class Profile
{
public int UserID { get; set; }
public User User { get; set; }
public int ProfileID { get; set; }
public string ProfileName { get; set; }
public DateTime CreateDate { get; set; }
public DateTime LastUpdateDate { get; set; }
}
I want to have both Navigation property and Foreign Key in both the entities.
This gives me error. What can do I in Fluent Mapping API to make this work?
Use this:
public class User
{
public string ID { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public Profile Profile { get; set; }
}
public class Profile
{
[Key, ForeignKey("User")]
public int ProfileID { get; set; }
public string ProfileName { get; set; }
public DateTime CreateDate { get; set; }
public DateTime LastUpdateDate { get; set; }
public User User { get; set; }
}
That is the only valid way to build one-to-one relation in EF - PK of the dependent entity must be also FK to principal entity. There is nothing like bidirectional one-to-one relation in EF because it cannot work in EF.
The way how people sometimes overcome this are two one-to-many relations where principal doesn't have navigation collection for dependent entities + manually defined unique keys in the database. That require manual mapping:
public class User
{
public string ID { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
// one side MUST be nullable otherwise you have bidirectional constraint where each
// entity demands other side to be inserted first = not possible
public int? ProfileId { get; set; }
public Profile Profile { get; set; }
}
public class Profile
{
public int ProfileID { get; set; }
public string ProfileName { get; set; }
public DateTime CreateDate { get; set; }
public DateTime LastUpdateDate { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
And in mapping you will define:
modelBuilder.Entity<User>
.HasOptional(u => u.Profile)
.WithMany()
.HasForeignKey(u => u.ProfileId);
modelBuilder.Entity<Profile>
.HasRequired(u => u.User)
.WithMany()
.HasForeignKey(u => u.UserId);
Now you must define Unique keys in the database - if you are using code first use custom database initializer. Be aware that still bidirectional one-to-one is wrong concept because both sides demand unique FK where NULL is still included in unique values so once you insert User before Profile there mustn't be any other User without Profile. That probably leads to serializable transaction.