Let me give a great example which will illustrate my problem.
Let's say I am building an application where many users can post different type of 'posts' (i.e. Photo, Status, etc). Let's just use Photo and Status for this case.
I will show you how I am currently modeling my data, and if this can be improved (and what I am doing wrong)
I have a generic Post class:
public class Post<T>
{
public Guid Id { get; set; }
public User Owner { get; set; }
public DateTime CreatedDate { get; set; }
public PostType Type { get; set; }
public T Data { get; set; }
}
Then I have a PostType enum:
public enum PostType
{
Photo,
Status
}
Then I have my respective Photo and Status classes
public class Photo
{
public string Url { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
public class Status
{
public string Text { get; set; }
}
I know that if I pursue my current solution to modeling this data, I will run into problems.
I have already hit some pain points, such as how do I return the latest 25 posts, regardless of type and also how do I return a specific post by Id without specifying the type of post(because the user shouldn't care.
Am I modeling my data COMPLETELY incorrectly? If so, do you have any suggestions for improvement?
Both of your problems could be solved by having a base class independent of the post type:
public abstract class Post
{
public Guid Id { get; set; }
public User Owner { get; set; }
public DateTime CreatedDate { get; set; }
public PostType Type { get; set; }
}
Your Post class can then inherit from it:
public class Post<T> : Post
{
public T Data { get; set; }
}
The methods that should return any type of post can still return the correct type but the caller will access them as the base Post type and cast them when needed:
Post GetPostById(int id);
IEnumerable<Post> GetLatestPosts(int count);
how do I return the latest 25 posts, regardless of type
You can't, because in your design there is nothing common between a Photo and a Status, your have a generic Post<T>, but here Ts can't be proceeded in a batch. A better design would be something like this:
public interface IPostable
{
Guid Id { get; set; }
public DateTime CreatedDate { get; set; }
PostType PostType { get; }
}
public class Photo : IPostable
{
//special properties for photo
public string Url { get; set; }
//implement the interface
public PostType PostType { get { return PostType.Photo; } }
}
public class Status : IPostable
{
public string Text { get; set; }
public PostType PostType { get { return PostType.Status; } }
}
Then you always deal with IPostable in a batch.
how do I return a specific post by Id
According to the design above, you can easily get a IPostable instance by its id, since id is one of its property, and return strongly-typed instance by judging its PostType property:
public IPostable GetPost(Guid id)
{
//id is a property of IPostable, so everything is easy
}
//you can easily get the real type
public T GetPost<T>(Guid id)
{
IPostable post = GetThePostFirst();
if (post.PostType == PostType.Photo) return (Photo)IPostable;
//etc.
}
Related
When I post my object
{
"Title": "LookingForGroup",
"Description": "Descrptjasag",
"CreatorName":"thelo#mail.bg",
"Price":"4"
}
in postman , I get a json exception that says :
System.Text.Json.JsonException: A possible object cycle was detected
which is not supported. This can either be due to a cycle or if the
object depth is larger than the maximum allowed depth of 32.
My Post Class
public class Post
{
public string Id { get; set; }
public string Title { get; set; }
public ApplicationUser Creator { get; set; }
public string CreatorId { get; set; }
public string Description { get; set; }
public PostType PostType { get; set; }
public decimal Price { get; set; }
public ICollection<Bid> Bids { get; set; }
}
My Model
public class PostInputModel
{
public string Title { get; set; }
public string Description { get; set; }
public string Price { get; set; }
public string CreatorName { get; set; }
}
My Controller
[HttpPost]
public async Task<ActionResult<PostInputModel>> PostPost(PostInputModel input)
{
Post post = new Post()
{
Id = Guid.NewGuid().ToString(),
Title = input.Title,
Creator = _context.Users.Where(x => x.UserName == input.CreatorName).FirstOrDefault(),
Description = input.Description,
PostType = PostType.Help,
Price = 4
};
_context.Posts.Add(post);
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (PostExists(post.Id))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtAction("GetPost", post);
}
My User Class
public class ApplicationUser : IdentityUser
{
public ICollection<Bid> Bids { get; set; }
public ICollection<Post> FreelanceService { get; set; }
}
As I mentioned in comment I am assume your JsonSerializer goes into infinite loops, because of reference between user and posts. Every user contains posts and every post contains user. You can easily check that by using QuickWatch in debug mode.
You will get something like that
User
Post1
Post2
User
Post1
Post2
..
Generally it's not a good practice to return your EntityFramework models as result object. You should create DTO objects that will be returned instead of EF objects:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/data/using-web-api-with-entity-framework/part-5
Your DTO objects shouldn't contains references that leads to this infinite loop.
Other solution that I personally would not use is to inform serializer to dont serialize specific properties:
public class ApplicationUser : IdentityUser
{
[JsonIgnore]
public ICollection<Bid> Bids { get; set; }
[JsonIgnore]
public ICollection<Post> FreelanceService { get; set; }
}
If JsonIgnore attribute will not help, please try to use [IgnoreDataMember].
You have circular reference in your model: Post has an ApplicationUser, that has a collection of Post's. That's your problem! Use foreign keys so you can relate posts with users, without creating circular references. Create and ApplicationUserId, replace in Post model, and use it as a key. 1
JsonSerializer in .NET Core 3.0 does not support circular references. A proposal to support this is being worked on in #41002.
If you believe this is not a cycle and instead you just have a very deep heirarchy, you can set JsonSerializerOptions.MaxDepth to something larger than the default.
I would like to access the email of this class into another class that I am already instantiated on my render page.
Any help very much appreciated.
public class Access
{
public string Email { get; set; } //I want this value...
}
public class Types
{
public string Id { get; set; }
public string Sum { get; set; }
public string Addition { get; set; }
public string Email { get; set; } // ...to be this value inside my class TYPES
}
Well I think you can simply use a copy constructor for the Types class with the Access instance as parameter from which you want to copy the email value when creating a new Types instance.
From what you said it seems you don't care if its a particular instance (as your post is a bit confusing between class and instance)
public class Types
{
public string Id { get; set; }
public string Sum { get; set; }
public string Addition { get; set; }
public string Email { get; set; }
public Types(Access access)
{
Email = access.Email
}
}
Following is the api call that return the Agent object
[Route("GetAgentById")]
public Agent GetAgentById(int id)
{
//Restrict Class fields
return new Agent(id);
}
Agent class have a lot of fields (Let say 100 fields)
public class Agent
{
public int AgentId { get; set; }
public string AgentName { get; set; }
public bool IsAssigned { get; set; }
public bool IsLoggedIn { get; set; }
......
......
public Agent() { }
}
Is there a way to ignore class properties without using annotations. I just want to return some of the fields of agent object before returning the agent object from api call. Is there any way to doing this
Return anonymous object with just required properties, like:
return new { agent.AgentId, agent.AgentName }
or use DTOs (which would be architecturally more correct, specially if you're building complex solution), in this example with Automapper:
return Mapper.Map<AgentDTO>(agent);
But if you really want to use "opt-out" approach and serialize just small subset of your object, and if you're using JSON.NET, you can mark just properties which needs to be serialized:
[DataContract]
public class Agent
{
// included in JSON
[DataMember]
public int AgentId { get; set; }
[DataMember]
public string AgentName { get; set; }
// ignored
public bool IsAssigned { get; set; }
public bool IsLoggedIn { get; set; }
}
I have four MVC model layer domain classes.
namespace MvcMobile.Models.BusinessObject
{
public class Speaker
{
public int SpeakerID { get; set; }
public string SpeakerName { get; set; }
}
public class Tag
{
public int TagID { get; set; }
public string TagName { get; set; }
}
public class Seminar
{
public string Seminar_Code { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Room { get; set; }
}
public class Seminar_Detail
{
public string Seminar_Code { get; set; }
public int SpeakerID { get; set; }
public int TagID { get; set; }
public string DateAndTime { get; set; }
}
}
I would like to make CRUD operation by using these classes. So I create two VeiwModel Classes.
namespace MvcMobile.ViewModel
{
public class Seminar_Root_ViewModel
{
public Seminar_Subsidiary_ViewModel Seminars { get; set; }
public List<Speaker> Speakers { get; set; }
public List<Tag> Tags { get; set; }
}
public class Seminar_Subsidiary_ViewModel
{
public Seminar Seminar { get; set; }
public List<Seminar_Detail> Seminar_Detail { get; set; }
}
}
For Controller layer, I consider that I will use Seminar_Root_ViewModel to make the whole CRUD operation processes.
What I would like to ask is that Is this proper way or correct way?
If you have more elegant way to make model layer and ViewModel layer, Please let me get suggestion.
Every suggestion will be appreciated.
[updated]
Let's assume that I make master-Detail form design.
Speaker and Tag are just look-up tables for dropdownlist or some controls like that.
Seminar is Master Data and Seminar_Detail will be Item Grid Data.
So As for this scenario, all of this classes are needed for this program.
Please let me know if my thinking is wrong.
The only thing I can see is if you are not going to re-use your Seminar_Subsidiary_ViewModel view model you could skip it.
If you are going to need those two properties Seminar and Seminar_Detail on another view or ajax call, it's perfectly fine to have that kind of separation.
Personally I'm not a huge fan of _ on class name, but that have nothing to do with the question.
Consider this Poco:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Fullname { get; set; }
}
Now i want to implement a follow technique where a user may follow other users so basically its self Many to Many relationship
problem is i don't know how exactly i can achieve this in Entity Framework Code-First ?
I thought of a linker Table :
public class UserFollow
{
public int Id { get; set; }
public int Follower { get; set; }
public int Following { get; set; }
public DateTime FollowDate { get; set; }
}
i want to be able to get All Followers and Following from every User Object?
This is quite simple using EF code-first as you only need the User POCO:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Fullname { get; set; }
public ICollection<User> FollowedUsers { get; set; }
}
The collection means that a User is related to other users.
PS: I noted you added a timestamp in your solution example. To achieve that you should still add the collection changing the generic type to whatever suits your needs.
Hope it helps.