2

I have a class named Classroom which is something like:

    public class Classroom
    {
        [Key]
        public int ClassroomId { get; set; }

        public string ClassroomTitle { get; set; }

        public string AccessCode { get; set; }

        public string ColorPicker { get; set; }

        public LevelGroup LevelGroup { get; set; }
    }

The LevelGroup class is something like:

public class LevelGroup
    {
        public int MLevelId { get; set; }

        public int MGroupId { get; set; }

        public Level Level { get; set; }

        public Group Group { get; set; }
    }

In my API, I am trying to retrieve the data of type Classroom like:

 [HttpPost("AddClassroom")]
 public async Task<JsonResult> AddClassroom([FromBody] Classroom classroom)
 {
        if (!ModelState.IsValid)
        {
            return Json(new ApiMessage
            {
                HasError = true,
                Message = "InvalidModel",
            });
        }

        try
        {
            _context.Add(classroom);
            await _context.SaveChangesAsync();

            return Json(new ApiMessage
            {
                HasError = false,
                Message = "Success"
            });
        }
        catch (Exception e)
        {
            return Json(new ApiMessage
            {
                HasError = true,
                Message = e.Message
            });
        }
 }

From the POSTMAN, I tried to hit the API at some url and in the BODY, I've passed this object:

{
    "classroomTitle": "SWE",
    "accessCode": "6s1x4d1",
    "colorPicker": "blue",
    "levelGroup": {
        "mLevelId": 1,
        "mGroupId": 2
    }
}

But, This is not working. It says:

An exception occurred in the database while saving changes for context type 'mirror_api.Models.ApplicationDbContext'.
      Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
       ---> Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 19: 'UNIQUE constraint failed: LevelGroups.MGroupId, LevelGroups.MLevelId'.

How to solve this problem?

Shunjid Rahman
  • 409
  • 5
  • 17
  • 4
    The error is pretty clear, the columns ```LevelGroups.MGroupId, LevelGroups.MLevelId``` have a unique constraint, meaning they can only contain values that are unique and not already present. Either replace the rows, disable the unique constraint, or enable auto increment on the columns. – nll_ptr Feb 24 '20 at 18:10
  • Most probably you have already added such entities to the database. You should either change properties that are unique to new values in the request or just rework your method into something like https://stackoverflow.com/questions/5557829/update-row-if-it-exists-else-insert-logic-with-entity-framework – Eugene Podskal Feb 24 '20 at 18:10
  • Does this answer your question? [EF Core / Sqlite one-to-many relationship failing on Unique Index Constraint](https://stackoverflow.com/questions/43500403/ef-core-sqlite-one-to-many-relationship-failing-on-unique-index-constraint) – Eugene Podskal Feb 24 '20 at 18:13
  • @nullptr.t Well, I got your point. But the thing is, I don't want to insert a new row in LevelGroups table. I just want to add a new Row in Classroom table. And in classroom, I just want to figure out which LevelGroup it belongs to. An additional note is that LevelGroup class defined many to many relation of two other classes Level and Group. – Shunjid Rahman Feb 24 '20 at 18:15
  • @EugenePodskal I guess it doesn't – Shunjid Rahman Feb 24 '20 at 18:20
  • Most probably it actually does (at least partially). In any case without code from the `AddClassroom([FromBody] Classroom classroom` method there isn't much else we can help you with. Provide us with that code and, perhaps, a more detailed answer/duplicate can be given. – Eugene Podskal Feb 24 '20 at 18:26
  • @EugenePodskal Well, I have added the code snippets. Can you check please? – Shunjid Rahman Feb 24 '20 at 18:34
  • Have you added data to the Level and Group tables respectively? You have specified values for mLevelId and mGroupId in the json passed from postman,so you need to add data of these two Ids to these two tables,after excuting api,Classroom and LevelGroup will automatically add data. – LouraQ Feb 25 '20 at 09:10

1 Answers1

1

Based on Your comments I understand You want to save passed object but You do not want to save inner property of it as it violates some DB constraints.

What I would try is to detach those properties from EF tracking before saving so it won't mark classroom.LevelGroup as Added. You can see this example. You can also control which objects EF is tracking for changes by setting a proper state for each individual property that was added to the EF context (docs).

You also want to read this which nearly describes what You seek:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);
    context.Entry(existingBlog).State = EntityState.Unchanged;

    // Do some more work...  

    context.SaveChanges();
}

but instead of attaching the object, You want to add it and then set one of its properties as EntityState.Detached to ignore it completly (or EntityState.Unchanged to keep tracking it but tell EF that there is nothing to save here). Something more like this:

...
_context.Add(classroom);
_context.Entry(classroom.LevelGroup).State = EntityState.Detached;
await _context.SaveChangesAsync();
...

The moment You add the object, EF "gegins tracking the given entity, and any other reachable entities" and You can "Use State to set the state of only a single entity" as shown.

Eatos
  • 444
  • 4
  • 12