Best approach for my EF 'DataBase' - entity-framework-4.1

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);

Related

Circular references error when pulling JSON entities in EF Core

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>().

Omitting null/default values in wcf rest template 40

I have a web service that uses the WCF REST Template 40. The way my data is set up, there are no [DataContract] or [DataMember] attributes on anything, it is just the class and its public properties. Example:
public class Permission : ServiceClass
{
public int PermissionID { get; set; }
public string PermissionName { get; set; }
public string PermissionCode { get; set; }
public string PermissionDescription { get; set; }
public bool IsActive { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int SystemID { get; set; }
}
This works fine except that if a property is null, e.g the two DateTime objects, the json still contains those values. I would like for them to be omitted. I have tried to add the [DataMember(EmitDefaultValue=false)] and [DataMember(IsRequired=true)] (i'm not using the default serializer when reading in, so I don't think I need that anyway) and it doesn't seem to work. Has anyone had any experience with this and know some kind of workaround?
[DataMember] attributes are only enforced if the class is also decorated with [DataContract]. You can do that, but once you go to the data contract route, then the serialization becomes an "opt-in" model: you'll need to declare the other members with [DataMember] as well:
[DataContract]
public class Permission : ServiceClass
{
[DataMember]
public int PermissionID { get; set; }
[DataMember]
public string PermissionName { get; set; }
[DataMember]
public string PermissionCode { get; set; }
[DataMember]
public string PermissionDescription { get; set; }
[DataMember]
public bool IsActive { get; set; }
[DataMember(EmitDefaultValue = false)]
public DateTime? StartDate { get; set; }
[DataMember(EmitDefaultValue = false)]
public DateTime? EndDate { get; set; }
[DataMember]
public int SystemID { get; set; }
}
Also, since this contract is now part of the data contract model, your base type (ServiceClass) will likely have to be changed to use the data contract as well.

what is dataAnnotation attribute use to make column have single character

what is DataAnnotation attribute i can use to make gander column only have one character in my table
public class Student
{
public int ID { get; set; }
[Required, MaxLength(50)]
public string Name { get; set; }
[DataType(DataType.Date)]
public DateTime Birthday { get; set; }
public char Gander { get; set; }
}
Use the Column attribute.
public class Student
{
public int ID { get; set; }
[Required, MaxLength(50)]
public string Name { get; set; }
[DataType(DataType.Date)]
public DateTime Birthday { get; set; }
[Column(TypeName = "NVARCHAR(1)")]
public char Gander { get; set; }
}
This should work:
[MaxLength(1)]
public string Gender { get; set; }
The problem is that char is not supported type in mapping and without change in EF core to support the type directly or introducing some simple type mapping or mapped conversions you are not able to map such property.

EF Problems with Navigation Properties and Mapping

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; }
}

Creating BiDirectional One - One relationship in Entity Framework 4.1 Code First

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.