Entity Framework Core domain and classes - c#

I'm currently following a project being developed by csharpfritz (of microsoft). It's called, "corewiki". Some form of "wikipedia" like project.
Here's the link to the repository on github: CoreWiki by Jeff Fritz
On the domain class for comments he writes this:
I'm trying to understand why he created an entity FromDomain class and ToDomain class:
// Main model
public class CommentDAO
{
public int Id { get; set; }
public int ArticleId { get; set; }
public virtual ArticleDAO Article { get; set; }
public string DisplayName { get; set; }
public string Email { get; set; }
[NotMapped]
public Instant Submitted { get; set; }
public string Content { get; set; }
}
public static CommentDAO FromDomain(Core.Domain.Comment comment)
{
return new CommentDAO
{
AuthorId = comment.AuthorId,
Content = comment.Content,
DisplayName = comment.DisplayName,
Email = comment.Email,
Id = comment.Id,
ArticleId = comment.ArticleId,
Submitted = comment.Submitted
};
}
public Core.Domain.Comment ToDomain()
{
return new Core.Domain.Comment
{
AuthorId = AuthorId,
Content = Content,
DisplayName = DisplayName,
Email = Email,
Id = Id,
ArticleId = this.Article.Id,
Submitted = Submitted
};
}

That's simply mapping code to map a domain model to a data access object and vice versa. You can implement this in many ways, such as the author showed, or using explicit conversion operators, or using a tool like AutoMapper.
See for example Having the domain model separated from the persistence model (first Google hit for "why separate domain model from dao") for an explanation of why you'd want that.

Related

How to conditionally hide a model property

I have a domain model to represent a "Resource". This is used by rest API endpoints for CRUD operations.
public class ResourceDto
{
[ScaffoldColumn(false)]
public int ResourceId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string ResourceTypeName { get; set; }
public string ResourceStatusName { get; set; }
public static implicit operator ResourceModel(ResourceDto resource)
{
return new ResourceModel()
{
FirstName = resource.FirstName,
LastName = resource.LastName,
ResourceType = new ResourceTypeModel()
{
Name = resource.ResourceTypeName
},
ResourceStatus = new ResourceStatusModel()
{
StatusName = resource.ResourceStatusName
}
};
}
}
I want to hide the ResourceId property in the case of the Create endpoint because it is a auto generated value in the database. In this example I don't want them sending a value for ResourceId.
But they will need to get the Id coming back from a Read so they can supply it for an Update or Delete.
I've looked at the json method ShouldSerialize but that will only hide it coming out from the Domain to the end point.
I've tried data annotations. [ScaffoldColumn(false)] didn't seem to do anything and [JsonIgnore] hides it completely.
The only thing I can think of is to create two Resource models, one with the id and one without. If I'm thinking about this the wrong way, I welcome some redirection as well. Thanks.

Multiple Model In MVC

I am learning how to use MVC right now and I just have a question on when I am creating and updating entries in the database. I was reading a post from this page: asp.mvc 4 EF ActionResult Edit with not all fields in view
The guy in it said to create a model that will be used, so is the efficient way to insert a new row and update an existing row by having two models with different properties?
So my models would look like this -
public class UserModelView
{
public string FirstName { get; set; }
public string Surname { get; set; }
public DateTime AccountCreated { get; set; }
public DateTime? LastLoggedIn { get; set; }
}
public class UserModelCreate
{
[Key]
public int UserId { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public DateTime AccountCreated { get; set; }
}
public class UserModelUpdate
{
public string FirstName { get; set; }
public string Surname { get; set; }
public DateTime? LastLoggedIn { get; set; }
}
Is this the best way to do what I need to do?
Im guessing you were previously using the entity class when binding your model back in.
You shouldn't do that!
The guy in the post is right, this is a much better way of controlling your entity and model information and provides a layer of seperation between the two.
After all you wouldnt want a user being able to directly manipulate an entity via a HTTP request.
I answered something similar here

how to assign DbEntities object to my model object in EF Database first

I am doing a project for my course work, I am using database first entity framework and got my DBContext class as
public partial class StudentDBEntities : DbContext
{
public DbSet<LoginDetail> LoginDetails { get; set; }
public DbSet<StudentDetail> StudentDetails { get; set; }
public DbSet<UserFriend> UserFriends { get; set; }
}
Now I created a model object as below to use this model to pass on view.
public class ViewStudentDetail
{
public StudentDetail Student { get; set; }
public UserFriend Friends { get; set; }
}
here is my StudentDetail class created default
public partial class StudentDetail
{
public StudentDetail()
{
this.UserFriends = new HashSet<UserFriend>();
}
public string StudentName { get; set; }
public string UnivName { get; set; }
public string City { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string EmailId { get; set; }
public virtual ICollection<UserFriend> UserFriends { get; set; }
}
In my View I have find a friend scenario and text box value passed is ViewStudentDetail.Friends.username.to the post method
My problem is that I want to assign the StudentDetails with the given username from DbContext object (that are already saved in database during registration) to the model object
In My controller i have like this
StudentDBEntities DbAccess = new StudentDBEntities();
ViewStudentDetail Studentdetails = new ViewStudentDetail();
Studentdetails.Student = DbAccess.StudentDetails .Find(username);
This assignment is not working and is assigning null.
Could you please tell me how to fetch the values in db to model object. Also if i need to assign each attribute to model please help me with the syntax to fetch from db object.
The DbSet.Find() method searches only for PRIMARY KEY matches, it will not search all/any properties on an entity.
I think you are looking for something like this:
ViewStudentDetail.Student = DbAccess.StudentDetails.FirstOrDefault(student => student.Username == username);
If not, please provide more of your source code in your question. Specifically, please include the generated entity classes.

User is duplicated when calling Reply

I'm trying to make a simple forum using MVC and I can't figure out why the user that is posting the reply is getting duplicated.
Here is the Reply Action:
[HttpPost]
public ActionResult Reply(string Title, string Content,int ReplyTo)
{
Post masterPost = db.Posts.FirstOrDefault(p => p.PostID == ReplyTo);
Post post = new Post();
post.PostID = 0;
post.CreatedOn = DateTime.Now;
post.ModifiedOn = DateTime.Now;
post.ReplyTo = masterPost;
post.Forum = db.Forums.FirstOrDefault(f=>f.ForumID == masterPost.Forum.ForumID);
post.User = (User)Session["User"];
post.Title = Title;
post.Content = Content;
//if (ModelState.IsValid)
//{
db.Posts.Add(post);
db.SaveChanges();
return RedirectToAction("View", "Posts", new { id = ReplyTo });
//}
return View(post);
}
This is the Post entity:
public class Post
{
[Key]
public int PostID { get; set; }
public string Title { get; set; }
[DataType(DataType.MultilineText)]
public string Content { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
public virtual Forum Forum { get; set; }
public virtual User User { get; set; }
public virtual Post ReplyTo { get; set; }
}
This is the User entity:
public class User
{
public int UserID { get; set; }
public string Name { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public DateTime RegisteredOn { get; set; }
}
Whenever the ReplyTo action is called it creates the Post but it also duplicates the User that is stored in the session (with a different UserID).
What am I doing wrong?
Even though you don't state it, it looks like you use Entity Framework.
If so, the culprit is probably this line
post.User = (User)Session["User"];
The "User" you store in session is now disconnected from Entity Framework, so EF assumes it is a brand-new user.
There are several ways to solve this. The one I prefer is to also add a UserId property to your Post class and use that
public class Post
{
// Stuff
public virtual int UserId { get; set; }
}
Then do:
post.UserId = ((User)Session["User"]).Id;
Entity Framework uses a convention to understand that you want to link that user to that post.
So when you say "it also duplicates the User", I conclude what you mean is that you get a duplicate User type in the DB after the db.SaveChanges.
Given that as my presumption, then I would speculate that the User stored in Session["Usser"] does not have an existing UserID property set, AND/OR your EF model does not have the Post to User multiplicity association set. Ordinarily, I would expect to see UserId property in Post, and your sample above does not show this.
Check your model to make sure you have the assocation set between User and Post, and that there's a foreign key property linking Post to User. The Post object requires a foreign key referencing UserId.
Take a look at Step 5 in this example from Microsoft.

Updating FK relationships in Entity Framework 4.1

I think I have read every article and stack overflow question regarding this, but cannot work out the solution. Let me start out with my models
public class Entry
{
public Entry ()
{
DateEntered = DateTime.Now;
}
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Email { get; set; }
public string Number { get; set; }
public string FbId { get; set; }
[ReadOnly(true)]
public DateTime DateEntered { get; set; }
public string AccessToken { get; set; }
//Relationsips
public Backgrounds Background { get; set; }
public Cars Car { get; set; }
}
public class Backgrounds
{
public int Id { get; set; }
public string Name { get; set; }
public string Filename { get; set; }
}
public class Cars
{
public int Id { get; set; }
public string Name { get; set; }
public string FileName { get; set; }
}
Now in my controller, I am updating the entry. Like follows
// PUT /api/entries/5
public HttpResponseMessage Put(Entry entry)
{
if(ModelState.IsValid)
{
_db.Entries.Attach(entry);
_db.Entry(entry).State = EntityState.Modified;
_db.SaveChanges();
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
My Entry model gets updated correctly, but if for eg entry.Background.Name changes, this will not be persisted to the database. My controller is accepting the entire entry model including its relationships => Backgrounds and Cars. However any value that is changed to the relationship is not updated or reflected. Any elegant solution without having to query the database then updating? I dont want to have any extra queries or lookups before I update.
Thanks
Tyrone
You must manually tell EF about all changes done to the object graph. You told EF just about change to entry instance but you didn't tell it about any change to related entities or relations itself. There is no elegant way to solve this. You have generally two options:
You will use some DTOs instead your entities and these DTOs will have some flag like IsDirty - when you receive object graph back to your controller you will reconstruct entities from DTOs and set their state based on IsDirty. This solution needs further extensions for example if your client can also delete relations.
You will query object graph from database and merge your incoming changes to entities retrieved from database.
There are some partial solutions like forcing to save changes to all related objects by setting their state to modified and identifying new objects by Id == 0 but again these solutions work only in specific scenarios.
More complex discussion about this problem.

Categories