tags:

views:

100

answers:

3

Similar to how database connections made within the scope of a TransactionScope will behave accordingly, I want to be able to test inside my class whether it is within an instance of another class?

My Scenario

I have a class (EntityBase) that all database objects are built upon, and has methods such as AddToDatabase, UpdateInDatabase, DeleteFromDatabase.

I want to create a BulkLoader class, that will handle inserts in a bulk fashion. This will then have a complete method, like the TransactionScope class, which will be the point that the data is loaded to the database.

This will require changing the EntityBase class so that it behaves differently if it is being instanitated within the scope of a BulkLoader class, and interacting with that class.

Example code:

using (BulkLoader bulk = new BulkLoader(connection, transaction))
{
    foreach (IToDatabase assignment in assignmentsCollection)
    {
        assignment.WriteToDataBase(connection, transaction);
    }

    bulk.Complete();
}

class ClientAssignment : IToDatabase
{
     public int WriteToDataBase(IDbConnection connection, 
         IDbTransaction transaction)
     {
          foreach (EntityBase account in accountsCollection)
          {
                account.AddToDataBase(connection, transaction);
          }

          foreach (EntityBase person in personsCollection)
          {
                person.AddToDataBase(connection, transaction);
          }
     }
}

class EntityBase
{
    public virtual int AddToDatabase(IDbConnection connection, 
        IDbTransaction transaction)
    {
        // question relates to this bit
        if (inBulkLoaderClass)
        {
            // interact with bulk loader
        }
        else 
        {
            // do existing code
        }
    }
}
+4  A: 

You could use ThreadStaticAttribute:

public class BulkLoader : IDisposable
{
    [ThreadStatic]
    private static BulkLoader currentBulkLoader;

    public BulkLoader()
    {
        if (InBulkLoader)
        {
            throw new InvalidOperationException();
        }
        currentBulkLoader = this;
    }

    public void Dispose()
    {
        currentBulkLoader = null;
    }

    public static bool InBulkLoader
    {
         get { return currentBulkLoader != null; }
    }

    public static BulkLoader CurrentBulkLoader
    {
         get { return currentBulkLoader; }
    }
}

I'm not generally keen on thread statics, but I think it's probably okay in this case. The check in the constructor makes sure you'll never be in a "nested" bulk loader. If you want to be able to nest them, keep a counter instead (increment in the constructor, decrement in the Dispose call).

Jon Skeet
Thanks Jon, how can I get a reference to the current instantiated BulkLoader from anywhere else? I assume this would have to be via a static method on the BulkLoader class, but that's where I get stuck.
ck
Editing answer...
Jon Skeet
Thanks Jon, that seems so simple. I'll implement it and see if it works as intended.
ck
+1  A: 

Well, there are some things you can do with ambient objects (in particular thread-static fields), but it isn't pretty. I'd argue that if the logic is different, it should be a separate method in the first place. Also, note that one point of view is that entity objects shoudn't be talking to the database anyway - their job is to represent the state of an object; it is the job of a repository class (or similar) to worry about persistance. Then you repository would have two strategies to cope with things. Perhaps there is an IBulkRepository interface that the repo could (optionally) implement, etc.

Marc Gravell
I think you're right - changing the AddToDatabase method to have an overload where I pass in the BulkLoader object may be easier, although I have cases where I have lots of calls within scope that would all have to be changed.
ck
Sometimes we need to be pragmatic, so maybe the thread-static approach has a place... I'll be happy either way, as long as you make a conscious decision, aware of the alternatives ;-p
Marc Gravell
Looking back at my application, the BulkLoader would be declared in a different layer of code to the AddToDatabase method, therefore this wouldn't work without changing some of my business objects. Question updated...
ck
A: 

I agree with Marc here. We too go with almost the same approach that Marc has shown. I guess that's the standard and optimal way to come to solution of such a problem. Though I am not much acquainted with thread static business here, so can't suggest more in that.

Kunal S