2
votes

I'm having an Azure WebJob running continuously which is doing CRUD operations in my database. I'm using Entity Framework and UnitOfWork pattern and in my WebJob I use Autofac to inject my dependencies, service and repository layer. I'm having some issues with stale data when running my WebJob.

Example: I update a record on my website and my WebJob is then kicked off but my WebJob can't see this change in the database. It sees the record prior to the change.

To fix this I tried to inject my custom context like this:

builder.RegisterType<PCContext>().As<IPCContext>().InstancePerDependency();

After doing that I can see the newest changes in the database. But now I have another issues. When I insert a new record and then read it, from my WebJob I can't see this new record. This worked fine before I injected my context (as shown in code above).

If I create a new context in my WebJob function I can read the updates from the database, but I want to use my service layer instead like this:

_services.UserExport.ExportUsers();

I can't figure out what I'm doing wrong here. Basically what I want is every time my WebJob function is kicked off I want a new context to be created so I'm sure I have the newest updates from the database and I want to be able to insert into my database and read this again in my WebJob using my service layer.

Can someone point me in the right direction?

Note that my WebJob is continuous so it's Autofac registration code is only executed once when the WebJob is start, not for every time a function in the WebJob is executed.

Please let me know if more description or code is necessary.

Thanks.

1
Wow, I am getting the exact problem and observed it few hours back. I would also like to know the solution. - Rahul Patil
Are you calling SaveShanges on your DbContext? - Steven
Yes calling SaveChanges but not saving to database. - Høgsdal

1 Answers

0
votes

According to your description, I tested the similar scenario on my side and I found I could read and update from my database. I defined my generic Repository and UnitOfWork as follows, you could refer to them:

Repository:

public interface IRepository<T>
{
    T GetById(object id);

    IQueryable<T> GetAll();

    void Edit(T entity);

    void Insert(T entity);

    void Delete(T entity);

}

public class Repository<T> : IRepository<T> where T : class
{
    public DbContext context;
    public DbSet<T> dbset;
    public Repository(DbContext context)
    {
        this.context = context;
        dbset = context.Set<T>();
    }

    public T GetById(object id)
    {
        return dbset.Find(id);
    }

    public IQueryable<T> GetAll()
    {
        return dbset;
    }

    public void Insert(T entity)
    {
        dbset.Add(entity);
    }

    public void Edit(T entity)
    {
        context.Entry(entity).State = EntityState.Modified;
    }

    public void Delete(T entity)
    {
        context.Entry(entity).State = EntityState.Deleted;
    }
}

UnitOfWork:

public class UnitOfWork : IDisposable
{
    private DbContext _context;
    private Repository<TodoItem> toDoItemRepository;

    public Repository<TodoItem> ToDoItemRepository
    {
        get
        {
            if (toDoItemRepository == null)
                toDoItemRepository = new Repository<TodoItem>(_context);
            return toDoItemRepository;
        }
    }

    public UnitOfWork() : this(new BruceDbContext()) { }
    public UnitOfWork(DbContext context)
    {
        _context = context;
    }

    public void Commit()
    {
        _context.SaveChanges();
    }

    #region Dispose
    private bool disposed = false;
    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    #endregion
}

For my WebJob I defined the Functions.cs and initialized the JobActivator as follows:

Functions.cs

public class Functions
{
    private UnitOfWork _unitOfWork;
    public Functions(UnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public async Task CronJob([TimerTrigger("0/30 * * * * *")] TimerInfo timer, CancellationToken cancelToken)
    {
        //retrieve the latest record
        var item = _unitOfWork.ToDoItemRepository.GetAll().OrderByDescending(i => i.CreateDate).FirstOrDefault();
        Console.WriteLine($"[{item.CreateDate}] {item.Text}");

        //insert a new record
        _unitOfWork.ToDoItemRepository.Insert(new Entities.TodoItem()
        {
            Id = Guid.NewGuid().ToString(),
            CreateDate = DateTime.Now,
            Text = $"hello world -{DateTime.Now}"
        });
        _unitOfWork.Commit();

        //retrieve the previous added record
        item = _unitOfWork.ToDoItemRepository.GetAll().OrderByDescending(i => i.CreateDate).FirstOrDefault();
        Console.WriteLine($"[{item.CreateDate}] {item.Text}");
    }
}

Program.cs

var builder = new ContainerBuilder();
builder.Register<UnitOfWork>(c => new UnitOfWork(new BruceDbContext())).InstancePerDependency();
builder.RegisterType<Functions>();
var container = builder.Build();

var config = new JobHostConfiguration()
{
    JobActivator = new AutoFacJobActivator(container)
};
var host = new JobHost(config);