0

I have to refactor an application that has a lot of services that depend on each other.

The problem I face is that all services use entities as if they were simple objects like this:

var lst = _context.chapters.Include(some condition).where(some condition).ToList();
foreach(var i in lst)
   i.somearray.removeall(some condition);
return _mapper.Map<datatype>(lst)

Now another service calls that method to get that data and do something completely unrelated to the chapter table.(saving a status) What happens the moment that other service calls _context.SaveChanges()? Basically a wipe of half the database and the other half is inconsistent.

What would be the best practice to refactor services like that? And what would be the most elegant solution that allows me to avoid rewriting all of them?

For now im just refreshing everything in the change tracker at critical points, but thats not very reliable and takes about 2 Minutes to complete. Setting the db context to transient doesnt work as some services seem to depend on the scoped context.

I think about creating dtos for every service and mapping them before they are altered. But its a massive application. That could take days.

1 Answers1

0

If I understand correctly, the problem is this line:

foreach(var i in lst)
   i.somearray.removeall(some condition);

I do agree that this can be a problem if the same DbContext is shared between services. As I see it you can approach the problem from different directions (in no particular order).

  1. Adjust the logic in the mapper. Make sure that the "reduction" of items in i.somearray is done in _mapper.Map and make sure that the mapper doesn't affect the entity itself. If you are using a library as AutoMapper this shouldn't be to hard. The problem here is that you have a condition, which I assume may be vary from service to service even tough the types are the same? In such a case you can pass mapping-instructions to your map and thus use the same mapping between services.

  2. Reduce the items returned in the query.

    var lst = _context.chapters.Include(p=> p.somearray.Where(somecondition)).where(some condition).ToList();

    A more concrete example can be found here. This also has the benefit of reducing the payload fetched from the database.

  3. Use no tracking. When enabled, entities will practically be read-only. Thus, any changes made to them will not be written back to db. Any other service will not be able to persist an entity fetched from another service (as long as it wasn't tracked). This is probably the easiest solution.

    _context.chapters.Include(some condition).where(some condition).AsNoTracking().ToList();

smoksnes
  • 10,509
  • 4
  • 49
  • 74
  • Thank you very much. Ill give 3 a shot asap. 1 sounds like the best solution and how I would expect it to work. But theres a lot of services and I might simply not get the budget to spend a week fixing them. – OriginalWombat Apr 19 '22 at 11:27
  • So, I used 3 in the end and it worked without a problem. Thank you! It was however not as easy as I thought, as I had to go through all services and understand each use of the context to check if tracking was needed. Saved me a lot of time anyway. – OriginalWombat Apr 28 '22 at 14:00
  • @OriginalWombat, glad it helped. Feel free to mark as answered if you consider the problem resolved. – smoksnes May 02 '22 at 06:49