GetConcurrencyPredicate doesn't get called?

Posts   
 
    
Philip
User
Posts: 17
Joined: 01-May-2009
# Posted on: 22-May-2009 22:00:24   

Hi there, I'm using the final build of 2.6.

I wonder if someone could give me a hand with this. I override CreateConcurrencyPredicateFactory() in my entity to return a custom predicate factory, but when I run my code (and debug it), the CreatePredicate() method in my custom factory never gets called.

To figure out why, I override GetConcurrencyPredicate to see what's happening in there, but that method is never run either. So I suspect I've done something wrong, but I don't know what. Shouldn't GetConcurrencyPredicate get called during the save process?

Here's what I have:

Simple code in a test method:

var md = new LinqMetaData();
var s = md.CreateResource("Study", "Data.EntityClasses.StudyEntity") as StudyEntity;
s.Itn = "boo";
md.SaveChanges();

Code in partial class StudyEntity:

protected override IConcurrencyPredicateFactory CreateConcurrencyPredicateFactory()
{
    return new StudyConcurrencyFilterFactory();
}

public override IPredicateExpression GetConcurrencyPredicate(ConcurrencyPredicateType predicateTypeToCreate)
{
    return base.GetConcurrencyPredicate(ConcurrencyPredicateType.Save);
}

I put a breakpoint on the return statement in GetConcurrencyPredicate(), but it never goes there.

I'm not putting my factory here as it doesn't seem important because it never gets run. It DOES the picked up by the entity because CreateConcurrencyPredicateFactory() gets run on entity initialization. But that's the extent to its use as far as I can tell.

Thanks for your help, Philip

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 23-May-2009 04:52:38   

The concurrency control is for updates and deletes. Your entity seems to be new, isn't?

BTW, why are you instantiating this way?

var md = new LinqMetaData();
var s = md.CreateResource("Study", "Data.EntityClasses.StudyEntity") as StudyEntity;
s.Itn = "boo";
David Elizondo | LLBLGen Support Team
Philip
User
Posts: 17
Joined: 01-May-2009
# Posted on: 26-May-2009 15:46:23   

Hi there,

I see. That makes sense. The code I included where I instantiate via CreateResource is in a test class that I have. Our data service uses LinqMetaData for everything, and the idea was to test concurrency. This code is not production code, although I am curious if there is a more recommended way of doing this.

At any rate, the issue is this:

I have an entity class called StudyEntity. It really only has two fields, an identity field in SQL Server and another field (called Itn) that I generate on save by overriding StudyEnitiy's OnSave() method. Itn has a unique constraint and is the field in concern. The OnSave method has logic that counts the current number of Study records in the DB and uses that as part of the the value for Itn. The problem is, if this method is called concurrently, I get a "Violation of UNIQUE KEY constraint", which of course I would expect in this case because both threads get the same record count from the DB.

Surrounding the code that does this in OnSave with a lock {} block doesn't seem to help, however surrounding the LinqMetaData.SaveChanges() method does work, but seems wrong.

lock (_lockObj)
{
  md.SaveChanges();
}

It seems now that the concurrency control predicate features won't do the trick in this case. If you have any thoughts about how to achieve my concurrency issues, I'd appreciate it.

Thanks a lot. Philip

MTrinder
User
Posts: 1461
Joined: 08-Oct-2008
# Posted on: 26-May-2009 21:39:45   

If you want genuine results from this sort of function that calculates something in the database then you HAVE to serialize access to the database whilst this value is being calculated. This means that only one process can be calculating this value at any time.

The only guaranteed way to do this is to run the code in a transaction with its isolation level set to serializable.

Matt

Philip
User
Posts: 17
Joined: 01-May-2009
# Posted on: 26-May-2009 22:13:54   

Thanks Matt, What I wound up doing is this:

public partial class StudyEntity
{
private static readonly Object OnSaveLock = new Object();
protected override void OnSave()
{
    Monitor.Enter(OnSaveLock);
    GenerateItn(this);
    base.OnSave();
}

protected override void OnDelete()
{
    Monitor.Enter(OnSaveLock);
    base.OnDelete();
}

protected override void OnTransactionCommit()
{
    Monitor.Exit(OnSaveLock);
}

protected override void OnTransactionRollback()
{
    Monitor.Exit(OnSaveLock);
}

private static void GenerateItn(StudyEntity studyEntity)
{
    var md = new LinqMetaData(studyEntity.Transaction);
    var startAt = md.Study.Count(st => st.TsInserted.Year == DateTime.Now.Year) + 1;
    studyEntity.Itn = DateTime.Now.Year.ToString().Substring(2) +
                      ("00" + startAt).Substring(startAt.ToString().Length - 1);
}

The one thing I don't understand fully yet is why OnTransactionRollback() gets run more than OnSave() when there's a sql server exception on commit. I tested what would happen if the GenerateItn method kept returning the same value. Instead of getting a nice unique constraint message, I get "Object synchronization method was called from an unsynchronized block of code." I found that it was happening because OnSave() was being run, say, twice, and OnTransactionRollback() was being run three times. When there are no errors, though, this code works well. I created two threads and had them compete to do inserts and it worked nicely.

Thanks, Philip

MTrinder
User
Posts: 1461
Joined: 08-Oct-2008
# Posted on: 26-May-2009 22:30:27   

But this solution does not cope with multiple instances of your application being run on different machines ? Or is this web/server based application ?

Matt

Philip
User
Posts: 17
Joined: 01-May-2009
# Posted on: 26-May-2009 22:36:30   

Yes, sorry. It is. This code is on a server and is accessed via a Silverlight client.

Philip

MTrinder
User
Posts: 1461
Joined: 08-Oct-2008
# Posted on: 26-May-2009 22:38:31   

Fair enough...simple_smile

Matt

Philip
User
Posts: 17
Joined: 01-May-2009
# Posted on: 26-May-2009 23:10:25   

Thanks for your help. I feel fairly confident now (with you having prodded me along) that I'm in a good place here.

Regards, Philip