INTRODUCTION
I am trying to learn how to use Entity framework on my own ( Code First approach ) by solving a small task that I have designed myself.
In order to understand my problem, you must be familiar with the content of the task I mentioned, so i will provide relevant information in the below section.
RELEVANT INFORMATION:
I have invented the following data model for a small quiz:
Each player answers 10 questions.
Each question has 3 possible answers, user chooses one (by clicking on the radio button, for example)
Only one answer is correct, other 2 are wrong.
PROBLEM:
I got stuck at implementing POCOs, so I need your advice on how to implement them properly.
I believe I did the basic stuff properly and that my main problem is in implementing navigational properties.
MY EFFORTS TO SOLVE THIS:
I do not have much to show. Still, my habit is to always show everything I have, in order to ease the task of the community.
Therefore, these are my unfinished POCOs:
public class Answer
{
public int AnswerId { get; set; }
public string TextOfTheAnswer { get; set; }
}
public class Question
{
public int QuestionId { get; set; }
public string TextOfTheQuestion { get; set; }
}
public class Player
{
public int PlayerId { get; set; }
public string Name { get; set; }
}
During writing of this post, I am using Google to learn as much as possible to solve my problem. If I make any headway I will update this post accordingly.
QUESTIONS:
How should I implement navigational properties to mirror the relationships from my data model?
Additionally, is there a way for me to enforce some of the imposed restrictions ( each question has 3 options; player answers on 10 different questions; only one answer is correct answer to the question; and so on...)?
I apologize if these questions may sound trivial to someone experienced. I am just beginning with C# and Entity framework, and can not wait to write anything that works. I hope you can all relate. Thank you for your understanding.
As for the navigational properties, here's something to get you started (let me know if there's something I have missed):
public class Answer
{
[Key]
public int AnswerId { get; set; }
public string TextOfTheAnswer { get; set; }
public int QuestionId{get;set;}
[ForeignKey(nameof(QuestionId))]
public virtual Question Question{get;set;}
}
public class Question
{
[Key]
public int QuestionId { get; set; }
public string TextOfTheQuestion { get; set; }
public virtual ICollection<Answer> Answers{get;set;}
public int CorrectAnswerId{get;set;}
[ForeignKey(nameof(CorrectAnswerId))]
public virtual Answer CorrectAnswer{get;set;}
}
public class SessionQuestion
{
[Key]
public int SessionQuestionId { get; set; }
public int QuestionId{get;set;}
[ForeignKey(nameof(QuestionId))]
public virtual Question Question{get;set;}
public int PlayerAnswerId{get;set;}
[ForeignKey(nameof(PlayerAnswerId))]
public virtual Answer PlayerAnswer{get;set;}
public int TriviaSessionId { get; set; }
[ForeignKey(nameof(TriviaSessionId))]
public virtual TriviaSession TriviaSession{ get; set; }
}
public class TriviaSession
{
[Key]
public int SessionId { get; set; }
public int PlayerId { get; set; }
[ForeignKey(nameof(PlayerId))]
public virtual Player Player{ get; set; }
public virtual ICollection<SessionQuestion> SessionQuestions{get;set;}
}
public class Player
{
[Key]
public int PlayerId { get; set; }
public string Name { get; set; }
public virtual ICollection<TriviaSession> TriviaSessions{get;set;}
}
Basically, EF creates subclasses of your classes at runtime, so leaving the navigation properties virtual lets the EF classes override them and obtain the reference according to the key which resides in the property whose name is the string passed to the ForeignKey attribute's constructor (quite a mouthful, huh?).
One to many navigation is easily created via declaring a virtual generic ICollection property.
Note that this model enforces the fact that only one question is correct- by design. As for the other restrictions, it sounds like business logic rules, not something you should have your data layer enforce.
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I have an Article entity in my database:
public class Article
{
public Guid Id { get; set; }
public string Heading { get; set; }
public string Author { get; set; }
public string Content { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
public int ViewsCount { get; set; }
public ImageData Image { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
For the creation I have ArticleInputModel, and for displaying the details view, I have ArticleDetailsModel, and for update I have ArticleUpdateModel (etc....)
However those models have the same properties.
Should I separate this much if it means repetitions of code?
I try to follow SRP but this seems like is breaking DRY principle?
Am I overlooking something and what?
Should I separate this much if it means repetitions of code?
Usually, you can identify three situations with potentially different sets of properties when working with model classes (Data Transfer Objects; DTOs) for a single entity:
entity creation
entity reading (displaying, viewing)
entity updating
However, there may be many more subtypes — e.g. different ways to create or update an entity, partial vs. full update, various kinds of displays, e.g. full view, some kind of partial views, view of an entity in a list etc.
It does make sense to have a system in constructing DTOs, such that you differentiate between the create, read (view), update DTOs in respect to your Create, Read, Update operations. You can see a clear parallel between such DTOs and CRU(D) operations (there's typically no DTO for the Delete operation).
Regardless of the particular naming you use, such categorizations help future maintainability of your code: if, in the future, you need to introduce a property that may not be set during entity creation, but can be altered during an update, or vice versa, it is easy to do without extensive changes to unrelated parts of code, e.g. you change the updating path only, but avoid changing the creating path.
I try to follow SRP but this seems like is breaking DRY principle?
Providing the model (DTOs) classes are semantically different, then I don't see this as a violation of DRY. However, this may be subjective.
Think of DTOs as secondary objects. The primary declaration is the database entity, which is part of your data model. The various views of such an entity in the form of DTOs are dependent on this entity declaration. As long as you keep it to a simple public SomeType PropName { get; set; } in the DTOs, it is not a violation of DRY you couldn't live with. In addition, it makes sense to e.g. keep comments explaining various properties in entity declarations only, and not duplicate them into DTOs (unless you have to generate some API docs, but that's solvable with <inheritdoc/> as well). What's important, is the clear distinction between entities and DTOs and their roles.
If you're creating a new instance of an Article, what is it's Id?
Or as a more clear example, what will it's UpdatedOn date be?
How do you update something that doesn't exist yet?
One other issue you might come across very quickly is how are you going to return a list of all the articles by a particular Author?
In the Article table you should be storing Author as an Id linking as a foreign key to the Author table (assuming there can only be a single Author).
If your article table now looks like this...
public class Article
{
public Guid Id { get; set; }
public string Heading { get; set; }
public Id Author { get; set; }
public string Content { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
public int ViewsCount { get; set; }
public ImageData Image { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
...you might begin to see where separate ViewModels/DTOs come into play.
Create
public class CreateArticle
{
public string Heading { get; set; }
public IEnumerable { get; set; }
public string Content { get; set; }
public string Image { get; set; }
}
You're creating a new Article so will probably be inserting an auto generated Guid as the key. You'll also be fairly likely to be taking the current date/time as the CreatedOn date. Author would come from a lookup list of some description so you'd need to pass some sort of list into the View (simplified as IEnumerable above). The image is most likely going to be supplied from a path to the image location so you'd maybe want to display as a text box.
Add
public class AddArticle
{
public string Heading { get; set; }
public Id Author { get; set; }
public string Content { get; set; }
public ImageData Image { get; set; }
}
When you've filled in your Create form, you now want to add it to the db. In this case your DTO needs to add data in the format the db expects. So you'd now be passing the selected Author Id and maybe the ImageData after some processing magic elsewhere.
You still don't need an Article Id or CreatedOn as these will be added once this DTO has validated.
Details and View
Hopefully you're now seeing the slight differences that make the ViewModel a valuable asset. You might also require something like the following to show the details of an Article as opposed to viewing the Article itself:
public class DetailOfArticle
{
public Guid Id { get; set; }
public string Heading { get; set; }
public Author Author { get; set; }
public string Content { get; set; }
public string CreatedOn { get; set; }
public string UpdatedOn { get; set; }
public int ViewsCount { get; set; }
}
public class ViewArticle
{
public Guid Id { get; set; }
public string Heading { get; set; }
public string Author { get; set; }
public string Content { get; set; }
public string CreatedOn { get; set; }
public string UpdatedOn { get; set; }
public int ViewsCount { get; set; }
public ImageData Image { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
Notice that the details might pass in an Author entity so that you can supply more information (this could also be exploded out into separate properties). You might also want to pass the date (and/or time) as a string after formatting etc.
The Article detail probably wouldn't need the comments as it's essentially the meta-data about the Article whereas the Article view is the Article as you'd want to present it for reading.
I have been using NHibernate on a system for sometime and I am quite happy with how it works, but I thought I would have a go at switching NHibernate out and putting in Entity Framework purely for a learning exercise. However there is a problem I have come across though, in my domain I have 2 classes (somewhat simplified for examples)
public class Post
{
public Post()
{
Comments = new List<Comment>();
}
public virtual int Id { get; set; }
public virtual string Title { get; set; }
public virtual string Text { get; set; }
public virtual DateTime DatePosted { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public virtual int Id { get; set; }
public virtual string CommentText { get; set; }
public virtual Post Post { get; set; }
}
The mappings for this work fine when I am using NHibernate, I can quite happily traverse between my Post Comment one to many relationship, Comments are lazy loaded as expected and all is good.
But when moving to EntityFramework it seems in order for the relationship to work, I need to change my Comment class to include PostId field as well as the Post object in order to get the relationship as such.
public class Comment
{
public virtual int Id { get; set; }
public virtual string CommentText { get; set; }
public virtual int PostId { get; set; } // added in for entityframework
public virtual Post Post { get; set; }
public virtual int UserId { get; set; }
}
With this field added into my domain object the mappings now seem to work, but I feel slightly uneasy about this as it feels like Entityframework if forcing me to change my domain, and I was under the impression that the domain model should know nothing of the persistence layer.
So do I really need this extra PostId field added in to my Comment class to get the relationship to work or am I doing something wrong?
Am I just being to pedantic about the domain being affected by the change in persistence layer?
Doesn't having the Post and PostId fields together like this mean that if say you change PostId, you will also have to handle the change to update Post field or vice versa in the Comment class?
Thanks
CD
In my opinion, this is one of the major deficiencies of Entity Framework. You can get EF to work without adding the foreign key, however your application will have to retrieve the entity from the database in order to set the property because EF does not have the equivalent of NHibernate's ISession.Load method.
I am doing code first and using a Table-per-type design. I am getting the following error when I extend the second object to multiple tables:
A value shared across entities or associations is generated in more than one location. Check that mapping does not split an EntityKey to multiple store-generated columns.
My database looks like:
Thanks for the up-vote, editing to add my picture:
The POCO for the project looks like:
public abstract class Project {
public int ProjectID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public virtual ICollection<LocationElement> LocationElements { get; set; }
public abstract string getProjectIdentifier();
}
And for a Location Element:
public enum HowObtainedCodes {
Provided = 1,
Estimated = 2,
Summarized = 3
}
public abstract class LocationElement {
public int LocationElementID { get; set; }
public int ProjectID { get; set; }
public HowObtainedCodes HowObtainedCodeID { get; set; }
}
And for a point:
[Table("ProvidedPoints")]
public class ProvidedPoint : LocationElement {
public double Lat { get; set; }
public double Long { get; set; }
public string Description { get; set; }
}
The link from projects (abstract) to scientific licences works fine, and my objects load / persist as expected. Further I can add LocationElements object in if I make it not abstract. As soon as I extend LocationElements and try to save a ProvidedPoint object I get the above message. My first thought was that the LocationElementID on ProvidedPoints was set as an Identity column, but this was not the case.
My question is: Am I doing something unexpected by trying to link two TPT objects together in this way? Am I missing something else?
As noted by #leppie above, I had to decorate the LocationElement class with the annotation [Table("LocationElements")], which immediately fixed the problem. My understanding with EF was that this was not necessary with the base table for a TPT design, and further I had not done it on the Project / ScientificLicence pair (that is, I only decorated the ScientificLicence object).
I am assuming this has something to do with the way LocationElements are added/persisted when I save a new Project object. If anyone has any additional insight I would love to know more.
Hope this helps someone else and a big thank you to leppie!
I've read several articles about bunch of EF and DTO, and I need some clarification about using EF Code First and DTO in n-tier scenario with WCF.
Let's look a these classes:
public class Order
{
public int Id { get; set; }
public DateTime ShipDate { get; set; }
public ObservableCollection<OrderDetail> Details { get; private set; }
}
public class OrderDetail
{
public int Id { get; set; }
public int OrderId { get; set; }
public int ProductId { get; set; }
public decimal Quantity { get; set; }
}
When user want to edit existing order, my client application (WPF MVVM app) requests some DTO, which then being converted to Order instance. Then, user makes some changes in order through UI - e.g., changes ShipDate, removes two positions, modifies one, and adds one.
Now I want to deliver changes to the server. As far as I understand DTO concept, I need to construct some DTO type, containing info about changes has been made:
[DataContract]
public class UpdateOrderDTO
{
[DataMember]
public DateTime ShipDate { get; set; }
[DataMember]
public Collection<OrderDetail> NewDetails { get; private set; }
[DataMember]
public Collection<OrderDetail> ModifiedDetails { get; private set; }
[DataMember]
public Collection<OrderDetail> DeletedDetails { get; private set; }
}
But when, and where should I to create this DTO? I mean, I can't create it on submitting changes - there's no change tracking information in Order class.
Looks like, this object have to be created together with Order after it was requested for edition by user. This allows to track changes... Am I wrong?
Please note, that the question isn't about STEs. For some reasons, I don't want/can't use them in current project.
Thanks a lot for sharing your experience.
I asked a question recently, and quite frankly, from the answer that was given, I am second guessing my entire strategy/how I design the classes and database.
I have not yet used the virtual keyword, nor Icollection at all in any of my Entity Framework projects, and quite frankly, after reading about it in some of the examples I do not fully understand why it is needed, or how it works.
In a sample application, I have a simple design where there are three lists - people, notes and pictures. The relationships are such that a person can own multiple notes and pictures, as well as people having a logo which is a picture.
public class Person
{
public int ID { get; set; }
public string name { get; set; }
public Picture logo { get; set; }
}
public class Note
{
public int ID { get; set; }
public string Text { get; set; }
public Person Owner { get; set; }
}
public class Picture
{
public int ID { get; set; }
public string Path { get; set; }
public Person Owner { get; set; }
}
When I want to select a list of notes that a person owns, I simply perform db.Notes.Where(x=>x.owner=="y") on the notes object. I think I understand that if I were to use Icollection on the person class, I could instead perform something along the lines of db.person.select(x=> x.notes) to retrieve all the notes. Am I correct in this thinking?
If you were in my position with the relatively simple example above, how would you build the classes (involving ICollection, virtual or anything else)?
In addition and most importantly, the above is just an example, however in my actual application, I have used a very similar structure where I use my custom type as the "connector"/Foreign Key.
In many examples I have been reading, (in the above example) they would be using public int OwnerID instead of public person Owner. This has really thrown me and I am questioning my entire EF strategy. What are the differences?
Any advice would be greatly appreciated.
I think you are making this more difficult that is needed. If you were laying out regular classes you would relate them to each other rather than finding related id's and loading them separately which you are doing in your example.
public class Person
{
public int ID { get; set; }
public string name { get; set; }
public ICollection<Note> Notes { get; set; }
public ICollection<Picture> Pictures { get; set; }
public Picture logo { get; set; }
}
public class Note
{
public int ID { get; set; }
public string Text { get; set; }
public Person Owner { get; set; }
}
public class Picture
{
public int ID { get; set; }
public string Path { get; set; }
public Person Owner { get; set; }
}
So now say you have gotten your person object using the query
var person = _context.People.Where(m=>m.ID=randomIntWeWant).First();
We can get all related items as properties.
For Notes
person.Notes
For Photos
person.Photos
ICollection is related to lazy loading. By declaring a property as ICollection on one side, your are saying you have a many-to-one relationship between the objects. If you declare a property as ICollection on both sides, you are saying it is a many-to-many relationship. EF takes care of creating the tables that track that relationship.