0

I am trying to apply patch operation in Data Layer Repository but due to have no Model State available in Data Layer i am unable to update it. I have debug the overall logic i come to the conclusion that patch.ApplyTo() operation update the Model but context.SaveChanges() is not updating the database context.

Below is the context class

public class ApplicationDbContext : DbContext, IApplicationDbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> opts) : base(opts)
        {

        }
        public override int SaveChanges()
        {
            return base.SaveChanges();
        }

    }

Below is the repositroy method code

public class SpotWriteOnly : ISpotWriteOnly {
    private IApplicationDbContext _context;
    public SpotWriteOnly(IApplicationDbContext context)
    {
        _context = context;
    }
    public int UpdateSpot(long id, JsonPatchDocument<SpotData> patch) {
        Spot s = _context.Spots.Include(x => x.Agent)
                        .Include(x => x.Channel)
                        .Include(x => x.Timeband)
                        .Include(x => x.Region)
                        .Include(x => x.Sponsor)
                        .Include(x => x.SpotType)
                        .Include(x => x.Status)
                        .Include(x => x.SpotStatusReason)
                        .OrderByDescending(x => x.Version)
                        .FirstOrDefault(x => x.SpotId == id && x.IsDeleted == false);
        SpotData sData = new SpotData { Spot = s };
        patch.ApplyTo(sData);
        int response = _context.SaveChanges();
        return response;
    }
}

Below is the Handler Class

public class UpdateSpotQueryHandler : IRequestHandler<UpdateSpotQuery, int> {
    public ISpotWriteOnly _repository;
    public IMapper _mapper;
    public UpdateSpotQueryHandler(ISpotWriteOnly repository, IMapper mapper)
    {
        _repository = repository;
        _mapper = mapper;
    }

    public Task<int> Handle(UpdateSpotQuery query, CancellationToken cancellationToken) {
        return Task.FromResult(_repository.UpdateSpot(query.SpotId, query.Patch));
    }

}

Below is the Query

    public class UpdateSpotQuery : IRequest<int> {
        public long SpotId { get; set; }
        public JsonPatchDocument<SpotData> Patch { get; set; }
    }

Below is the controller code

    [HttpPatch("{id}")]
    public IActionResult UpdateSpot(long id,[FromBody] JsonPatchDocument<SpotData> patch) {
        if(ModelState.IsValid) {
            var result = Mediator.Send(new UpdateSpotQuery { SpotId = id, Patch = patch} );
            var response = new Envelop
                {
                    IsSuccessful = true,
                    StatusCode = 200,
                    StatusMessage = "OK",
                    Response = result.Result,
                    isExceptionGenerated = false,
                    Exception = null,
                };
            return Ok(response);
        }
        else {
            var response = new Envelop
                {
                    IsSuccessful = false,
                    StatusCode = 200,
                    StatusMessage = "Error in Model",
                    Response = ModelState,
                    isExceptionGenerated = true,
                    Exception = new Exception[] { new ArgumentException()},
                };
            return BadRequest(response);
        }
    }

I am trying to update database using patch operation in data layer.

Nikita Chayka
  • 2,057
  • 8
  • 16
Assay Khan
  • 56
  • 9
  • The question is unclear and shows confusion about what Entity Framework is. There's no PATCH in databases, that's an HTTP verb used for updating resources.. EF is an ORM and the code you posted *creates* a new object, it doesn't update an existing one. – Panagiotis Kanavos Nov 23 '22 at 08:18
  • There are no "handler" classes either in ASP.NET Core or EF Core by the way. No "repositories" either - a DbContext itself is a multi-entity repository and Unit-of-Work already. Assuming `_context` is an actual DbContext, the code you posted loads an object from the database, creates a *new* SpotData and INSERTs that new object. The `JsonPatchDocument` – Panagiotis Kanavos Nov 23 '22 at 08:18
  • Show us the `ApplyTo` method. – Alexander Petrov Nov 23 '22 at 08:22
  • @AlexanderPetrov [`JsonPatchDocument.ApplyTo`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.jsonpatch.jsonpatchdocument.applyto?view=aspnetcore-7.0#microsoft-aspnetcore-jsonpatch-jsonpatchdocument-applyto(system-object)) – Rafalon Nov 23 '22 at 08:24
  • Given there are no "repositories" in ASP.NET Core or EF Core we have no idea what your "data layer repository" does. At best, it's just a wrapper over EF Core, in which case you're *INSERT*ing a self-referencing `SpotData` instance. Definitely not what you wanted, but the database will get modified. Worst case, the "repository" is breaking EF's Unit-of-Work behavior by opening a database transaction and forgetting to close it. A DbContext *is* a UoW - no changes are saved until SaveChanges is called. When that's called, all pending changes are stored in a single internal transaction. – Panagiotis Kanavos Nov 23 '22 at 08:27
  • To check how HTTP Patch and EF Core work **remove all extra classes**. No "repositories", no "handlers". Follow the [JSON PATCH tutorial](https://learn.microsoft.com/en-us/aspnet/core/web-api/jsonpatch?view=aspnetcore-7.0) instead. Just create a Web API with a DbContext and in the controller's Patch action, load the object you want, apply the patch to that object and then call `context.SaveChanges`. Nothing more. Once you get that to work, think about whether you really need extra stuff to solve your *real* problems. I suspect you'll find book architectures are a bit too much for real life – Panagiotis Kanavos Nov 23 '22 at 08:32
  • @PanagiotisKanavos I don't see any insert being made here. `sData` isn't related to `_context`. The entity being tracked here is `s`, and I think user expects `patch.ApplyTo(sData)` to actually update values inside `s` (which *is* being tracked), as `s` should be the same as `sData.Spot` – Rafalon Nov 23 '22 at 08:34
  • Here is my ApplicationDbContext Class : **bold** public class ApplicationDbContext : DbContext, IApplicationDbContext { public ApplicationDbContext(DbContextOptions opts) : base(opts) { } public override int SaveChanges() { return base.SaveChanges(); } } **bold** – Assay Khan Nov 23 '22 at 08:36
  • @Rafalon you're right, the code is creating the new class but then discards it. Which means the "repository" is somehow interfering, perhaps due to an extra database transaction. We can't guess what else is going on in code that wasn't posted – Panagiotis Kanavos Nov 23 '22 at 08:36
  • @AssayKhan the problem is neither EF Core or ASP.NET Core. It's all the other code that was added on top. First you need to see how PATCH really behaves. Create a new project and follow the [JsonPatch in ASP.NET Core web API](https://learn.microsoft.com/en-us/aspnet/core/web-api/jsonpatch?view=aspnetcore-7.0) tutorial. You'll see it just works. Replace `CreateCustomer` with code that loads the object you want from the database, `var spot = _contest.Spots(id);` and then call `SaveChanges` at the end. You'll see the object is updated – Panagiotis Kanavos Nov 23 '22 at 08:37
  • controller method `[HttpPatch("{id}")] public IActionResult UpdateSpot(long id,[FromBody] JsonPatchDocument patch) { if(ModelState.IsValid) { var result = Mediator.Send(new UpdateSpotQuery { SpotId = id, Patch = patch} ) var response = new Envelop { Response = result.Result, }; return Ok(response); } else { return BadRequest(response); } }` – Assay Khan Nov 23 '22 at 08:41
  • @AssayKhan don't post your code in comments. Edit your question to add it there instead – Rafalon Nov 23 '22 at 08:42
  • @PanagiotisKanavos book architecture is a real pain but thats also help to think ahead no pain no gain :) – Assay Khan Nov 23 '22 at 08:45

0 Answers0