JsonException: Cycle detected when using PostMan - c#

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.

Related

EF Core In Memory Database not saving ICollection object column

I have read that InMemory Database with EF Core has limitations and was considering moving to sqlite for development, however, I wanted to know if this behavior is a limitation of InMemory Database or if it's me. I have tried reading the documentation but can't find material explicitly mentioning this. I have the following:
// Startup
services.AddDbContext<StudentResultsContext>(opt =>
opt.UseInMemoryDatabase("StudentResultsList"));
// context
public class StudentResultsContext : DbContext
{
public StudentResultsContext(DbContextOptions<StudentResultsContext> options)
: base(options)
{
}
public DbSet<StudentResults> StudentResultsList { get; set; }
}
// classes
public class StudentResults
{
public long ID { get; set; }
public long ParentId { get; set; }
public string Name { get; set; }
public ICollection<ExamScores> Results { get; set; }
}
public class ExamScores
{
public long ID{ get; set; }
public long StudentId { get; set; }
public Exam ExamType { get; set; }
public double Score { get; set; }
}
// controller
[HttpPost]
public async Task<ActionResult<StudentResults>> PostStudentResults(StudentResults studentResults)
{
_context.StudentResultsList.Add(studentResults);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetStudentResults), new { id = studentResults.ID }, studentResults);
}
Results is saved as null in the database up even though the return from post claims they were created, like so
The post
What comes back from get
Is this something I did or a problem with InMemory Databse?
I guess you don't do anything to load related data and thus you get null for Results.
Try to get saved entity with context.StudentResultsList.Include(s => s.Results).
Check Loading Related Data for other strategies.

EF Core how to create relational model

I'm a beginner on Asp.Net Core and I need to do de following:
I have the models curso and unidade, they share a many to many relationship.
Both models work fine by themselves, my problem however is that I can't make the relational model.
The idea is to make an endpoint that receives an object with one unidade and an array of curso and the API would add or remove the relationships accordingly.
The following code is what I have made so far, I'm getting an error Identity_Insert.
Am I on the right direction with this? Or is there another proper better way of doing this?
Modelo de curso
public class Curso
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Required]
[Key]
public long curId { get; set; }
[Required]
[StringLength(80)]
public string curDescricao { get; set; }
[Required]
[StringLength(1)]
public string curStatus { get; set; }
[StringLength(20)]
public string curCodExterno { get; set; }
[StringLength(60)]
public string curObservacao { get; set; }
}
Modelo de unidade
public class Unidade
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Required]
[Key]
public long uniId { get; set; }
[Required]
[StringLength(80)]
public string uniDescricao { get; set; }
[Required]
[StringLength(1)]
public string uniStatus { get; set; }
[StringLength(20)]
public string uniCodExterno { get; set; }
public byte[] uniImagem { get; set; }
}
Modelo de CursoUnidade
public class CursoUnidade
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Required]
[Key]
public long cuuId { get; set; }
/*[Required]
public long cuuCurId { get; set; }
[ForeignKey("cuuCurId")]*/
public List<Curso> Curso { get; set; }
/*[Required]
public long cuuUniId { get; set; }
[ForeignKey("cuuUniId")] */
public Unidade Unidade { get; set; }
}
Serviço de unidade
public void AddTeste(CursoUnidade cursoUnidade)
{
_contexto.Add(cursoUnidade);
_contexto.SaveChanges();
}
The problem is that the Curso and Unidade instances on the CursoUnidade that's being passed in are not being tracked by EF. As a result, when you attempt to add it and save, it's attempting to create those two entities again. However, since they both already have ids, it cannot do that and fails.
This is one of the many problems associated with trying to save what's passed into your action directly. You should use a view model and then map that over the the actual entity you end up saving. This forces you to be explicit about what you're doing and results in less bugs such as this one. Also, unless you're actually intending the two related instances to be able to modified at the same time they're being associated, it doesn't make sense to post the entire entity, anyways. This can easily lead to an overpost exploit, where the user makes modifications to entities they shouldn't be able to make, and because of the sloppy handling of the post server-side, those just get blindly saved to the database.
Instead, use a view model/DTO like:
public class CursoUnidadeDTO
{
public long CursoId { get; set; }
public long UnidadeId { get; set; }
}
Then, in your action:
public void AddTeste(CursoUnidadeDTO cursoUnidade)
{
var curso = await _contexto.Cursos.FindAsync(cursoUnidade.CursoId);
var unidade = await _contexto.Unidades.FindAsync(cursoUnidade.UnidadeId);
if (curso == null || unidade == null)
{
return BadRequest("Cannot create relationship");
}
_contexto.Add(new CursoUnidade { Curso = curso, Unidade = unidade });
await _contexto.SaveChangesAsync();
}
Or, if your CursoUnidade class had explicit foreign key properties, you wouldn't even need to look anything up:
_contexto.Add(new CursoUnidade { CursoId = cursoUnidade.CursoId, UnidadeId = cursoUnidade.UnidadeId });
Alternatively, you can simply attach these entities first, so that EF knows about them:
_contexto.Attach(cursoUnidade.Curso);
_contexto.Attach(cursoUnidade.Unidade);
_contexto.Add(cursoUnidade);
await _contexto.SaveChangesAsync();
That will solve your immediate issue, but like I said previously, it will also allow overposts, and if that's not something you're explicitly allowing, could be very dangerous.

Modeling multiple post types

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

ASP.NET MVC 3: why does it think my class is a complex type?

Building my project using Code First EF.
I have a User class that has, as one of its properties, List<FriendGroup> (where a FriendGroup is basically just a collection of Users, kind of like 'Circles' in Google+). FriendGroup is defined in a different file as a POCO and... here's the thing... I never said anywhere that it is a ComplexType.
But when I try to run my application I get the exception,
System.InvalidOperationException: The type 'FriendGroup' has already been configured as an entity type. It cannot be reconfigured as a complex type.
I would be grateful for any insight anyone might be able to offer on why ASP.NET decided my class is a ComplexType. Thanks in advance!
ETA: relevant bits from my model:
namespace Clade.Models
{
public class User
{
[Key]
public int userID { get; private set; }
[Required]
public Profile profile { get; set; }
[Required]
public string userName { get; set; }
...
public List<FriendGroup> friendGroups { get; set; }
...
public List<AchievementEarned> achievementsEarned { get; set; }
}
}
namespace Clade.Models
{
public class FriendGroup
{
[Key]
[HiddenInput(DisplayValue = false)]
public int friendGroupID { get; private set; }
[HiddenInput(DisplayValue = false)]
public int userID { get; set; }
[Required]
public string friendGroupName { get; set; }
public Privacy defaultPrivacy { get; set; }
[Timestamp]
public DateTime dateCreated { get; set; }
public List<User> usersInFG { get; set; }
public void UpdateMe(FriendGroup editedFG)
{
friendGroupName = editedFG.friendGroupName;
defaultPrivacy = editedFG.defaultPrivacy;
usersInFG = editedFG.usersInFG;
}
}
}
There's also EF code, repositories, etc. but none of them know anything about the inner workings of any POCO. The only thing I see here that may be problematic is that User has a List<FriendGroup> and FriendGroup has a List<User>. But nothing has ever existed that annotated FriendGroup as a ComplexType.
ETA (2): Profile is also just a POCO:
namespace Clade.Models
{
public class Profile
{
[Key]
public int profileID { get; private set; }
public User user { get; set; }
public DiscussionGroup dg { get; set; }
public string location { get; set; }
public Privacy locationPrivacy { get; set; }
public string aboutMe { get; set; }
public Privacy aboutMePrivacy { get; set; }
...
}
}
User does have Lists of a couple of ComplexType-annotated objects, but EF did not complain about those.
namespace Clade.Models
{
[ComplexType]
public class AchievementEarned
{
public Achievement achievement { get; set; }
[Timestamp]
public DateTime dateEarned { get; set; }
}
}
ETA (3): Here's the method in UserRepository where the error occurs. It happens on the line which starts with var results.
public bool Add(User newUser)
{
bool rv = false;
//check to make sure no one else has the same username first
var results = from user in Users
where user.userName.Equals(newUser.userName)
select user;
if (results.Count() == 0)
{
this.context.Users.Add(newUser);
this.Commit();
rv = true;
}
return rv;
}
You may try the following:
Use int property instead of Enum
or try to update to EF 5 beta 2 with
PM> Install-Package EntityFramework -Pre
wich much better supports enums

EF 4.1 - Code First - JSON Circular Reference Serialization Error

I am getting an a Circular Reference Serialization Error although, to my knowledge I do not have any circular references. I am retrieving a set of Orders from the database and sending them to the client as JSON. All the code is shown below.
This is the error:
Error
A circular reference was detected
while serializing an object of type
'System.Data.Entity.DynamicProxies.Order_83CECF2AA4DE38232F9077D4B26941AB96BC61230419EA8AC42C9100E6072812'.
Description: An unhandled exception
occurred during the execution of the
current web request. Please review the
stack trace for more information about
the error and where it originated in
the code.
Exception Details:
System.InvalidOperationException: A
circular reference was detected while
serializing an object of type
'System.Data.Entity.DynamicProxies.Order_83CECF2AA4DE38232F9077D4B26941AB96BC61230419EA8AC42C9100E6072812'.
Source Error:
An unhandled exception was generated
during the execution of the current
web request. Information regarding the
origin and location of the exception
can be identified using the exception
stack trace below.
My classes are as follows:
Order
public class Order
{
[Key]
public int OrderId { get; set; }
public int PatientId { get; set; }
public virtual Patient Patient { get; set; }
public int CertificationPeriodId { get; set; }
public virtual CertificationPeriod CertificationPeriod { get; set; }
public int AgencyId { get; set; }
public virtual Agency Agency { get; set; }
public int PrimaryDiagnosisId { get; set; }
public virtual Diagnosis PrimaryDiagnosis { get; set; }
public int ApprovalStatusId { get; set; }
public virtual OrderApprovalStatus ApprovalStatus { get; set; }
public int ApproverId { get; set; }
public virtual User Approver { get; set; }
public int SubmitterId { get; set; }
public virtual User Submitter { get; set; }
public DateTime ApprovalDate { get; set; }
public DateTime SubmittedDate { get; set; }
public Boolean IsDeprecated { get; set; }
}
Patient
public class Patient
{
[Key]
public int PatientId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleInitial { get; set; }
public bool IsMale;
public DateTime DateOfBirth { get; set; }
public int PatientAddressId { get; set; }
public Address PatientAddress { get; set; }
public bool IsDeprecated { get; set; }
}
Certification Period
public class CertificationPeriod
{
[Key]
public int CertificationPeriodId { get; set; }
public DateTime startDate { get; set; }
public DateTime endDate { get; set; }
public bool isDeprecated { get; set; }
}
Agency
public class Agency
{
[Key]
public int AgencyId { get; set; }
public string Name { get; set; }
public int PatientAddressId { get; set; }
public virtual Address Address { get; set; }
}
Diagnosis
public class Diagnosis
{
[Key]
public int DiagnosisId { get; set; }
public string Icd9Code { get; set; }
public string Description { get; set; }
public DateTime DateOfDiagnosis { get; set; }
public string Onset { get; set; }
public string Details { get; set; }
}
OrderApprovalStatus
public class OrderApprovalStatus
{
[Key]
public int OrderApprovalStatusId { get; set; }
public string Status { get; set; }
}
User
public class User
{
[Key]
public int UserId { get; set; }
public string Login { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string NPI { get; set; }
public string Email { get; set; }
}
NOTE: ADDRESS CLASS IS NEW ADDITION DURING EDIT
Address
public class Address
{
[Key]
public int AddressId { get; set; }
public string StreetAddress { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string Phone { get; set; }
public string Title { get; set; }
public string Label { get; set; }
}
The code that executes the serialization is here:
Excerpt from OrderController
public ActionResult GetAll()
{
return Json(ppEFContext.Orders, JsonRequestBehavior.AllowGet);
}
Thanks
You could try to remove the virtual keyword from all navigation properties to disable lazy loading and proxy creation and then use eager loading instead to load the required object graph explicitely:
public ActionResult GetAll()
{
return Json(ppEFContext.Orders
.Include(o => o.Patient)
.Include(o => o.Patient.PatientAddress)
.Include(o => o.CertificationPeriod)
.Include(o => o.Agency)
.Include(o => o.Agency.Address)
.Include(o => o.PrimaryDiagnosis)
.Include(o => o.ApprovalStatus)
.Include(o => o.Approver)
.Include(o => o.Submitter),
JsonRequestBehavior.AllowGet);
}
Referring to your previous post it looks like your application isn't relying on lazy loading anyway because you introduced there the virtual properties to load the object graph lazily, possibly causing now the serialization trouble.
Edit
It's not necessary to remove the virtual keyword from the navigation properties (which would make lazy loading completely impossible for the model). It's enough to disable proxy creation (which disables lazy loading as well) for the specific circumstances where proxies are disturbing, like serialization:
ppEFContext.Configuration.ProxyCreationEnabled = false;
This disables proxy creation only for the specific context instance ppEFContext.
(I've just seen, #WillC already mentioned it here. Upvote for this edit please to his answer.)
When you know that you need to serialize from a particular context, you can disable proxy creation for that particular query like below. This has worked for me and is better than revising my model classes.
using (var context = new MeContext())
{
context.Configuration.ProxyCreationEnabled = false;
return context.cars.Where(w => w.Brand == "Ferrari")
}
This approach takes away the proxy object type for this particular instance of the context so the returned objects are the actual class and therefore serialization is not a problem.
ie:
{Models.car}
instead of
{System.Data.Entity.DynamicProxies.car_231710A36F27E54BC6CE99BB50E0FE3B6BD4462EC‌​A19695CD1BABB79605296EB}
The problem is that your are actually serializing an entity framework generated proxy object. Unfortunatly this has some issues when used with the JSON serializer. You might consider to map your entities to special simple POCO classes for the sake of JSON compatibility.
There is an attribute to add to Entity Framework objects
[ScriptIgnore]
This makes the code not perform Circular references.
I think they have fixed this in the latest version.
Check out the help docs under the section "Serializing and Deserializing JSON -> Serialization and Preserving Object References".
Set this setting when initializing the JSON.Net Serializer:
PreserveReferencesHandling = PreserveReferencesHandling.Objects;
So an example would be this:
var serializerSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects };
string json = JsonConvert.SerializeObject(people, Formatting.Indented, serializerSettings);
I verified that this works with my code first solution, and a circular reference in the navigation properties. If you look at the resulting JSON it should have "$id" and "$ref" properties everywhere.
An alternative solution would be to use anonymous types as the result of a LINQ query.
In my project, I am using lazy loading extensively, and disabling it was not the right thing to do.
An alternative solution, if only some values from objects are necessary, is build an anonymous class and return it, like the example below:
public JsonResult AjaxFindByName(string term)
{
var customers = context.Customers
.Where(c => c.Name.ToUpper().Contains(term.ToUpper())).Take(10)
.AsEnumerable()
.Select(c => new {
value = c.Name,
SSN = String.Format(#"{0:000\-00\-0000}", c.SSN),
CustomerID = c.CustomerID });
return Json(customers, JsonRequestBehavior.AllowGet);
}
The circular reference happens because you use eager loading on the object.
You have a couple of methods:
Turn off eager loading when your loading your Query (linq or lambda)
DbContext.Configuration.ProxyCreationEnabled = false;
Remove the virtual keyword from the Domainmodel
Detach the objects (= no eager loading functionality & no proxy)
Repository.Detach(entityObject)
DbContext.Entry(entityObject).EntityState = EntityState.Detached
Clone the properties
You could use something like AutoMapper to clone the object, don't use the ICloneable interface, because it also clones the ProxyProperties in the object, so that won't work.
In case you are building an API, try using a separte project with a different configuration (that doesn't return proxies)
PS. Proxies is the object that's created by EF when you load it from the Entity Framework. In short: It means that it holds the original values and updated values so they can be updated later. It handles other things to ;-)
For those using the proxy EF/Linq2SQL classes my solution was to simply remove the parent reference on my child entities.
So in my model, I selected the relationship and changed the Parent reference to be Internal rather than Public.
May not be an ideal solution for all, but worked for me.
You can remove the virtual keyword:
public virtual Patient Patient { get; set; } -> public Patient Patient { get; set; }
Keep in mind that when you remove the virtual keyword, lazy loading will be turned off.
I was able to solve this problem by using the method described here:
http://mytechworld.officeacuity.com/index.php/2010/02/serializing-entity-framework-objects-into-json-using-asp-net-mvc/

Categories

Resources