My code is written with C# and the data layer is using LINQ to SQL that fill/load detached object classes.
I have recently changed the code to work with multi threads and i’m pretty sure my DAL isn’t thread safe.
Can you tell me if PopCall() and Count() are thread safe and if not how do i fix them?
public class DAL
{
//read one Call item from database and delete same item from database.
public static OCall PopCall()
{
using (var db = new MyDataContext())
{
var fc = (from c in db.Calls where c.Called == false select c).FirstOrDefault();
OCall call = FillOCall(fc);
if (fc != null)
{
db.Calls.DeleteOnSubmit(fc);
db.SubmitChanges();
}
return call;
}
}
public static int Count()
{
using (var db = new MyDataContext())
{
return (from c in db.Calls select c.ID).Count();
}
}
private static OCall FillOCall(Model.Call c)
{
if (c != null)
return new OCall { ID = c.ID, Caller = c.Caller, Called = c.Called };
else return null;
}
}
Detached OCall class:
public class OCall
{
public int ID { get; set; }
public string Caller { get; set; }
public bool Called { get; set; }
}
Individually they are thread-safe, as they use isolated data-contexts etc. However, they are not an atomic unit. So it is not safe to check the count is > 0 and then assume that there is still something there to pop. Any other thread could be mutating the database.
If you need something like this, you can wrap in a
TransactionScopewhich will give you (by default) the serializable isolation level:Of course, this introduces blocking. It is better to simply check the
FirstOrDefault().Note that
PopCallcould still throw exceptions – if another thread/process deletes the data between you obtaining it and callingSubmitChanges. The good thing about it throwing here is that you shouldn’t find that you return the same record twice.The
SubmitChangesis transactional, but the reads aren’t, unless spanned by a transaction-scope or similar. To makePopCallatomic without throwing:Now the
FirstOrDefaultis covered by the serializable isolation-level, so doing the read will take a lock on the data. It would be even better if we could explicitly issue anUPDLOCKhere, but LINQ-to-SQL doesn’t offer this.