简体   繁体   中英

EF database concurrency

I have two apps: one app is asp.net and another is a windows service running in background.

The windows service running in background is performing some tasks (read and update) on database while user can perform other operations on database through asp.net app. So I am worried about it as for example, in windows service I collect some record that satisfy a condition and then I iterate over them, something like:

IQueryable<EntityA> collection = context.EntitiesA.where(<condition>)
foreach (EntityA entity in collection)
{
  // do some stuff
}

so, if user modify a record that is used later in the loop iteration, what value for that record is EF taken into account? the original retrieved when performed:

context.EntitiesA.where(<condition>)

or the new one modified by the user and located in database?

As far as I know, during iteration, EF is taken each record at demand, I mean, one by one, so when reading the next record for the next iteration, this record corresponds to that collected from :

context.EntitiesA.where(<condition>)

or that located in database (the one the user has just modified)?

Thanks!

There's a couple of process that will come into play here in terms of how this will work in EF.

  1. Queries are only performed on enumeration (this is sometimes referred to as query materialisation) at this point the whole query will be performed
  2. Lazy loading only effects navigation properties in your above example. The result set of the where statement will be pulled down in one go.

So what does this mean in your case:

//nothing happens here you are just describing what will happen later to make the 
// query execute here do a .ToArray or similar, to prevent people adding to the sql 
// resulting from this use .AsEnumerable
IQueryable<EntityA> collection = context.EntitiesA.where(<condition>); 
//when it first hits this foreach a 
//SELECT {cols} FROM [YourTable] WHERE [YourCondition] will be performed
foreach (EntityA entity in collection)
{
    //data here will be from the point in time the foreach started (eg if you have updated during the enumeration in the database you will have out of date data)
    // do some stuff
}

If you're truly concerned that this can happen then get a list of id's up front and process them individually with a new DbContext for each (or say after each batch of 10). Something like:

IList<int> collection = context.EntitiesA.Where(...).Select(k => k.id).ToList();
foreach (int entityId in collection)
{
    using (Context context = new Context())
    {
        TEntity entity = context.EntitiesA.Find(entityId);
        // do some stuff
        context.Submit();
    }
}

I think the answer to your question is 'it depends'. The problem you are describing is called 'non repeatable reads' an can be prevented from happening by setting a proper transaction isolation level. But it comes with a cost in performance and potential deadlocks.

For more details you can read this

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM