How to conditionally hide a model property - c#

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.

Related

Entity Framework Core domain and classes

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.

.net core : incomplete JSON response

I'm trying to build simple API for training, in my database I got users (firstname, lastname, email password, list<sports>) and sports ( name, userID).
All is okay when I want to get my users, I got an object populated with sports. But the JSON response is incomplete, it is "cut" in the middle.
[{"firstName":"Nicolas","lastName":"Bouhours","email":"n.bouh#test.com","password":"nico#hotmail.fr","sports":[{"name":"Trail","userId":1
This is my controller :
// GET: api/Users
[HttpGet]
public IEnumerable<User> GetUsers()
{
var users = _context.Users.Include(u => u.Sports).ToList();
return users;
}
And my models :
public class Sport : BaseEntity
{
public string Name { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
public class User : BaseEntity
{
public String FirstName { get; set; }
public String LastName { get; set; }
public String Email { get; set; }
public String Password { get; set; }
public List<Sport> Sports { get; set; }
}
public class SportAppContext : DbContext
{
public SportAppContext(DbContextOptions<SportAppContext> options) : base(options)
{ }
public DbSet<User> Users { get; set; }
public DbSet<Sport> Sports { get; set; }
}
I really don't understand what happen, if you have any idea
I'm running into the same issue right now. You can also change the JSON serialization/configuration settings to ignore self-reference loops, as shown in the accepted answer for this question
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddJsonOptions(options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
}
I had this problem to in one of my projects. This is caused by a self referencing loop.
You need to create some sort of DTO (Data Transfer Object) which will be used to generate your JSON.
In your DTO you remove the inverse relationship so you end up having something like
public class SportDto
{
public string Name { get; set; }
}
public class UserDto
{
public String FirstName { get; set; }
public String LastName { get; set; }
public String Email { get; set; }
public String Password { get; set; }
public List<SportDto> Sports { get; set; }
}
You then map your user User and Sport models to your UserDto and SportDto
A good tool for doing this mapping is AutoMapper. You can read the docs to see how to get started.
After the mapping is done, you Send the DTOs as your JSON and not your models.
Just to add another yet unique scenario where this can occur. This can also happen if your DAL is returning queryables. In my scenario, I was returning a boxed object from the DAL and had something like this as a linq query
...
RootLevelProp1 = "asd",
RootLevelProp2 = "asd",
Trades = b.Trades.OrderBy(c => c.Time).Select(c => new
{
c.Direction,
c.Price,
c.ShareCount,
c.Time
}) //<---- This was being returned as a queryable to the controller
The Trades query was never being executed even though it's root object had .ToListAsync() called on it. What was happening was that the controller would return the result but only up to the Trades section and the Json would not be terminated properly. I then realized an exception was being caught in some custom middleware I wrote in which it was complaining about the data reader already being open. Without going to deep into my investigation, I assumed it had to do something with the DI and how it was handling the lifecycle of the context. The fix was to just add ToList on the trades. It's an ugly way to pass data from the DAL but this is just a fun project.
In my case this solve my issue on core 3, by using Newtonsoft:
https://learn.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-3.0#add-newtonsoftjson-based-json-format-support
Prior to ASP.NET Core 3.0, the default used JSON formatters
implemented using the Newtonsoft.Json package. In ASP.NET Core 3.0 or
later, the default JSON formatters are based on System.Text.Json.
Support for Newtonsoft.Json based formatters and features is available
by installing the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet
package and configuring it in Startup.ConfigureServices.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddNewtonsoftJson();
}
The selected answer was correct in my case as well, my JSON response was getting truncated by a reference loop in my JSON response, and setting ReferenceLoopHandling.Ignore did indeed solve my issue. However, this is not the best solution in my opinion, as this maintains the circular references in your model. A better solution would use the [JsonIgnore] attribute within the model.
The issue in your model is here:
public class Sport : BaseEntity
{
public string Name { get; set; }
public int UserId { get; set; }
public User User { get; set; } //This is the cause of your circular reference
}
public class User : BaseEntity
{
public String FirstName { get; set; }
public String LastName { get; set; }
public String Email { get; set; }
public String Password { get; set; }
public List<Sport> Sports { get; set; }
}
As you can see, your User navigation property is where this response is truncated. Specifically, it will cause each Sport in the json response to contain all of the user information for each sport entry in the response. Newtonsoft does not like this. The solution is to simply [JsonIngore] the navigation properties that cause this circular reference. In your code this would be:
public class Sport : BaseEntity
{
public string Name { get; set; }
public int UserId { get; set; }
[JsonIgnore]
public User User { get; set; } //fixed
}
public class User : BaseEntity
{
public String FirstName { get; set; }
public String LastName { get; set; }
public String Email { get; set; }
public String Password { get; set; }
public List<Sport> Sports { get; set; }
}
Faced similar issue, response was getting truncated. Issue was a getter method which trying to formatting date.

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.

How implement a Model to force clients fill at least one of two inputs in ASP.NET MVC

This is My Model:
public class PhoneNumber {
public long Id { get; set; }
public string Tel1 { get; set; }
public string Tel2 { get; set; }
}
How can I force Clients in create action to fill at least One Tel (Tel1 or Tel2), I don't want to use any Clients Script, Or Controller Code, I just interested in use some attributes in above Model like [Required] to achieve this goal?
Just Change Model and use Custom Validator like this:
public class PhoneNumber : IValidatableObject {
public long Id { get; set; }
public string Tel1 { get; set; }
public string Tel2 { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var field1 = new[] { "Tel1" };
var field2 = new[] { "Tel2" };
if (string.IsNullOrEmpty(Tel1))
if (String.IsNullOrEmpty(Tel2))
yield return new ValidationResult("At least Fill one of Tel1 or Tel2‏", field1);
if (string.IsNullOrEmpty(Tel2))
if (String.IsNullOrEmpty(Tel1))
yield return new ValidationResult("At least Fill one of Tel1 or Tel2", field2);
}
}
This is similar to the following question which is answered here where it is suggested that you create a custom attribute.
Your model could then be written as:
[AtLeastOneRequired("Tel1", "Tel2", ErrorMessage="Please enter at least one value.")]
public class PhoneNumber {
public long Id { get; set; }
public string Tel1 { get; set; }
public string Tel2 { get; set; }
}
The description does describe writing javascript code to validate client side but this is optional if you only wanted to use server side validation.
There is no baked-in validation attribute for that but it is easy to write your own. I don't know what you mean by no client side code but it is impossible to do this kind of client side validation without any client side code (not sure if HTML5 validations is supporting this kind of validation).
Here is a sample custom validation creation step by step blog post for you:
ASP.NET MVC: LessThan and GreaterThan Validation Attributes

Categories