0
\$\begingroup\$

Let's suppose I have 2 repositories:

  • first is responsible for creating job and return jobId
  • second is responsible for creating log and take jobId as argument

My goal is to:

  • save Job and Log simultaneously
  • prevent situation when in case of error only Job would be saved without Log

What is the most recommended way to get desired result?

I prepared 3 cases which came to my mind but if you see better alternative please share it.

  • option 1 (getting result and save changes in controller)
public class JobRepository : IJobRepository
{
    private readonly Context _context;
    
    public JobRepository(Context context)
    {
        _context = context;
    }

    public Guid CreateJob()
    {
        var job = new Job { Id = Guid.NewGuid() };
        _context.Jobs.Add(job);
        return job.Id;
    }
}
// ...
public class LogRepository : ILogRepository
{
    private readonly Context _context;
    
    public LogRepository(Context context)
    {
        _context = context;
    }

    public void CreateLog(Guid id)
    {
        var log = new Log { Jobid = id };
        _context.Logs.Add(log);
    }
}
// ...
public class JobsController : Controller
{
    private readonly Context _context;
    private readonly IJobRepository _jobRepository;
    private readonly ILogRepository _logRepository;

    public JobsController(Context context, JobRepository jobRepository, ILogRepository logRepository)
    {
        _context = context;
        _jobRepository = jobRepository;
        _logRepository = logRepository
    }

    [HttpGet]
    public IActionResult Create()
    {
        return View();
    }

    [HttpPost]
    public IActionResult Create()
    {
        var id = _jobRepository.CreateJob();
        _logRepository.CreateLog(id);
        _context.SaveChanges();
        return RedirectToAction("Index");
    }
}
  • option 2 (inject one repository into another)
public class JobRepository : IJobRepository
{
    private readonly Context _context;
    private readonly ILogRepository _logRepository;
    
    public JobRepository(Context context, ILogRepository logRepository)
    {
        _context = context;
    }

    public void CreateJob()
    {
        var job = new Job { Id = Guid.NewGuid() };
        _context.Jobs.Add(job);
        _logRepository.CreateLog(job.Id);
        _context.SaveChanges();
    }
}
// ...
public class LogRepository : ILogRepository
{
    private readonly Context _context;
    
    public LogRepository(Context context)
    {
        _context = context;
    }

    public void CreateLog(Guid id)
    {
        var log = new Log { Jobid = id };
        _context.Logs.Add(log);
    }
}
// ...
public class JobsController : Controller
{
    private readonly IJobRepository _jobRepository;

    public JobsController(JobRepository jobRepository)
    {
        _jobRepository = jobRepository;
    }

    [HttpGet]
    public IActionResult Create()
    {
        return View();
    }

    [HttpPost]
    public IActionResult Create()
    {
         _jobRepository.CreateJob();
        return RedirectToAction("Index");
    }
}
  • option 3 (do not use context in controller but declare Save method in each repo)
public class JobRepository : IJobRepository
{
    private readonly Context _context;
    
    public JobRepository(Context context)
    {
        _context = context;
    }

    public Guid CreateJob()
    {
        var job = new Job { Id = Guid.NewGuid() };
        _context.Jobs.Add(job);
        return job.Id;
    }

    public void Save()
    {
        _context.SaveChanges();
    }
}
// ...
public class LogRepository : ILogRepository
{
    private readonly Context _context;
    
    public LogRepository(Context context)
    {
        _context = context;
    }

    public void CreateLog(Guid id)
    {
        var log = new Log { Jobid = id };
        _context.Logs.Add(log);
    }

    public void Save()
    {
        _context.SaveChanges();
    }
}
// ...
public class JobsController : Controller
{
    private readonly IJobRepository _jobRepository;
    private readonly ILogRepository _logRepository;

    public JobsController(JobRepository jobRepository, ILogRepository logRepository)
    {
        _jobRepository = jobRepository;
        _logRepository = logRepository
    }

    [HttpGet]
    public IActionResult Create()
    {
        return View();
    }

    [HttpPost]
    public IActionResult Create()
    {
        var id = _jobRepository.CreateJob();
        _logRepository.CreateLog(id);
        return RedirectToAction("Index");
    }
}
```
\$\endgroup\$
3
  • 2
    \$\begingroup\$ Unfortunately none of them is correct. If one of the save command fails then the other is not rolled back. So both changes should be tackled under the same DbContext to be able to participate in a same transaction. \$\endgroup\$ Nov 23 at 15:55
  • \$\begingroup\$ plus, it seems you're using EF as SaveChanges exists in DbContext, if that's true, then the best approach would be to override SaveChanges to log entities changes. \$\endgroup\$
    – iSR5
    Nov 23 at 16:25
  • 2
    \$\begingroup\$ This question seems better suited to StackOverflow or SoftwareEngineering.SE. This isn't a request for a code review. \$\endgroup\$
    – Flater
    Nov 24 at 13:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.