开发者

Initializing constructor from stored cache in C#

I'm not sure exactly how to describe this question, but here goes. I've got a class hierarch开发者_如何学JAVAy of objects that are mapped in a SQLite database. I've already got all the non-trivial code written that communicates between the .NET objects and the database.

I've got a base interface as follows:

public interface IBackendObject
{
    void Read(int id);
    void Refresh();
    void Save();
    void Delete();
}

This is the basic CRUD operations on any object. I've then implemented a base class that encapsulates much of the functionality.

public abstract class ABackendObject : IBackendObject
{
    protected ABackendObject() { } // constructor used to instantiate new objects
    protected ABackendObject(int id) { Read(id); } // constructor used to load object

    public void Read(int id) { ... } // implemented here is the DB code
}

Now, finally, I have my concrete child objects, each of which have their own tables in the database:

public class ChildObject : ABackendObject
{
    public ChildObject() : base() { }
    public ChildObject(int id) : base(id) { }
}

This works fine for all my purposes so far. The child has several callback methods that are used by the base class to instantiate the data properly.

I now want to make this slightly efficient. For example, in the following code:

public void SomeFunction1()
{
    ChildObject obj = new ChildObject(1);
    obj.Property1 = "blah!";
    obj.Save();
}

public void SomeFunction2()
{
    ChildObject obj = new ChildObject(1);
    obj.Property2 = "blah!";
    obj.Save();
}

In this case, I'll be constructing two completely new memory instantiations and depending on the order of SomeFunction1 and SomeFunction2 being called, either Property1 or Property2 may not be saved. What I want to achieve is a way for both these instantiations to somehow point to the same memory location--I don't think that will be possible if I'm using the "new" keyword, so I was looking for hints as to how to proceed.

Ideally, I'd want to store a cache of all loaded objects in my ABackendObject class and return memory references to the already loaded objects when requested, or load the object from memory if it doesn't already exist and add it to the cache. I've got a lot of code that is already using this framework, so I'm of course going to have to change a lot of stuff to get this working, but I just wanted some tips as to how to proceed.

Thanks!


If you want to store a "cache" of loaded objects, you could easily just have each type maintain a Dictionary<int, IBackendObject> which holds loaded objects, keyed by their ID.

Instead of using a constructor, build a factory method that checks the cache:

public abstract class ABackendObject<T> where T : class
{
     public T LoadFromDB(int id) {
         T obj = this.CheckCache(id);
         if (obj == null)
         { 
             obj = this.Read(id); // Load the object
             this.SaveToCache(id, obj);
         }
         return obj;
     }
} 

If you make your base class generic, and Read virtual, you should be able to provide most of this functionality without much code duplication.


What you want is an object factory. Make the ChildObject constructor private, then write a static method ChildObject.Create(int index) which returns a ChildObject, but which internally ensures that different calls with the same index return the same object. For simple cases, a simple static hash of index => object will be sufficient.


If you're using .NET Framework 4, you may want to have a look at the System.Runtime.Caching namespace, which gives you a pretty powerful cache architecture.

http://msdn.microsoft.com/en-us/library/system.runtime.caching.aspx


Sounds perfect for a reference count like this...

    #region Begin/End Update
    int refcount = 0;
    ChildObject record;
    protected ChildObject ActiveRecord
    {
        get 
        {
            return record;
        }

        set 
        {
            record = value;
        }
    }

    public void BeginUpdate()
    {
        if (count == 0)
        {
            ActiveRecord = new ChildObject(1);

        }

        Interlocked.Increment(ref refcount);
    }

    public void EndUpdate()
    {
        int count = Interlocked.Decrement(ref refcount);

        if (count == 0)
        {
            ActiveRecord.Save();
        }
    }
    #endregion


    #region operations

    public void SomeFunction1()
    {
        BeginUpdate();

        try
        {
            ActiveRecord.Property1 = "blah!";
        }
        finally
        {
            EndUpdate();
        }
    }

    public void SomeFunction2()
    {
        BeginUpdate();

        try
        {
            ActiveRecord.Property2 = "blah!";
        }
        finally
        {
            EndUpdate();
        }
    }


    public void SomeFunction2()
    {
        BeginUpdate();

        try
        {
            SomeFunction1();
            SomeFunction2();
        }
        finally
        {
            EndUpdate();
        }
    } 
    #endregion


I think your on the right track more or less. You can either create a factory which creates your child objects (and can track "live" instances), or you can keep track of instances which have been saved, so that when you call your Save method it recognizes that your first instance of ChildObject is the same as your second instance of ChildObject and does a deep copy of the data from the second instance over to the first. Both of these are fairly non-trivial from a coding standpoint, and both probably involve overriding the equality methods on your entities. I tend to think that using the first approach would be less likely to cause errors.

One additional option would be to use an existing Obect-Relational mapping package like NHibernate or Entity Framework to do your mapping between objects and your database. I know NHibernate supports Sqlite, and in my experience tends to be the one that requires the least amount of change to your entity structures. Going that route you get the benefit of the ORM layer tracking instances for you (and generating SQL for you), plus you would probably get some more advanced features your current data access code may not have. The downside is that these frameworks tend to have a learning curve associated with them, and depending on which you go with there could be a not insignificant impact on the rest of your code. So it would be worth weighing the benefits against the cost of learning the framework and converting your code to use the API.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜