1

I used the following approach long time (approx 5 years):

Create one big class with initialization of XXXEntities in controller and create each method for each action with DB. Example:

public class DBRepository
{
    private MyEntities _dbContext;
    public DBRepository()
    {
        _dbContext = new MyEntities();
    }

    public NewsItem NewsItem(int ID)
    {
        var q = from i in _dbContext.News where i.ID == ID select new NewsItem() { ID = i.ID, FullText = i.FullText, Time = i.Time, Topic = i.Topic };
        return q.FirstOrDefault();
    }

    public List<Screenshot> LastPublicScreenshots()
    {
        var q = from i in _dbContext.Screenshots where i.isPublic == true && i.ScreenshotStatus.Status == ScreenshotStatusKeys.LIVE orderby i.dateTimeServer descending select i;
        return q.Take(5).ToList();
    }

    public void SetPublicScreenshot(string filename, bool val)
    {
        var screenshot = Get<Screenshot>(p => p.filename == filename);
        if (screenshot != null)
        {
            screenshot.isPublic = val;
            _dbContext.SaveChanges();
        }

    }

    public void SomeMethod()
    {
            SomeEntity1 s1 = new SomeEntity1() { field1="fff", field2="aaa" };
            _dbContext.SomeEntity1.Add(s1);
            SomeEntity2 s2 = new SomeEntity2() { SE1 = s1 };
            _dbContext.SomeEntity1.Add(s2);
            _dbContext.SaveChanges();
    }

And some external code create DBRepository object and call methods. It worked fine. But now Async operations came in. So, if I use code like

    public async void AddStatSimplePageAsync(string IPAddress, string login, string txt)
    {
        DateTime dateAdded2MinsAgo = DateTime.Now.AddMinutes(-2);
        if ((from i in _dbContext.StatSimplePages where i.page == txt && i.dateAdded > dateAdded2MinsAgo select i).Count() == 0)
        {
            StatSimplePage item = new StatSimplePage() { IPAddress = IPAddress, login = login, page = txt, dateAdded = DateTime.Now };
            _dbContext.StatSimplePages.Add(item);
            await _dbContext.SaveChangesAsync();
        }
    }

can be a situation, when next code will be executed before SaveChanged completed and one more entity will be added to _dbContext, which should not be saved before some actions. For example, some code:

                DBRepository _rep = new DBRepository();
                _rep.AddStatSimplePageAsync("A", "b", "c");
                _rep.SomeMethod();

I worry, that SaveChanged will be called after line

        _dbContext.SomeEntity1.Add(s1);

but before

        _dbContext.SomeEntity2.Add(s2);

(i.e. these 2 actions is atomic operation)

Am I right? My approach is wrong now? Which approach should be used?

PS. As I understand, will be the following stack:

1. calling AddStatSimplePageAsync
2. start calling await _dbContext.SaveChangesAsync(); inside AddStatSimplePageAsync
3. start calling SomeMethod(), _dbContext.SaveChangesAsync() in AddStatSimplePageAsync is executing in another (child) thread.
4. complete _dbContext.SaveChangesAsync() in child thread. Main thread is executing something in SomeMethod()
Oleg Sh
  • 8,496
  • 17
  • 89
  • 159
  • I am not sure if I got your question. Looks like you are worried about the async operation that, if not completed while another request come, could give you some problem with code assignation? Could you elaborate it better? – Vincenzo Apr 13 '15 at 21:52
  • added details to main question – Oleg Sh Apr 13 '15 at 22:02
  • 1
    This is not how async/await operations work. Async/await will not move on to another function that requires the result of an `awaited` function call. Your current approach seems fine to me and for further reading, please check the MSDN, specifically the section I have linked to here - https://msdn.microsoft.com/en-us/library/hh191443.aspx#BKMK_WhatHappensUnderstandinganAsyncMethod – Tommy Apr 13 '15 at 22:05
  • In addition, your example edit, you are not awaiting your `AdStatSimplePageAsync` method, and as a result, it will run normally (synchronously). – Tommy Apr 13 '15 at 22:09
  • as I understand, method AddStatSimplePageAsync "returns control" to my main method (which will start _rep.SomeMethod() ) when await _dbContext.SaveChangesAsync(); is calling, but BEFORE complete. Look at the end of my question, I added something – Oleg Sh Apr 13 '15 at 22:17
  • Oleg, your question is unclear. By just looking at the code i could see a couple of things i'd do differently. Is there a reason your `AddStat` method is `async void` instead of `async Task`. And why aren't you awaiting on it? – Yuval Itzchakov Apr 13 '15 at 23:13

1 Answers1

2

Ok this time I (think)'ve got your problem.

At first, it's weird that you have two separate calls to SaveChangesmethod. Usually you should try to have it at the end of all your operations and then dispose it.

Even thought yes, your concerns are right, but some clarifications are needed here.

  1. When encountering an asyncor await do not think about threads, but about tasks, that are two different concepts.

  2. Have a read to this great article. There is an image that will practically explain you everything.

To say that in few words, if you do not await an async method, you can have the risk that your subsequent operation could "harm" the execution of the first one. To solve it, simply await it.

Community
  • 1
  • 1
Vincenzo
  • 1,549
  • 1
  • 9
  • 17