在C#中从存储的缓存初始化构造函数

时间:2010-06-25 17:06:08

标签: c# inheritance memory-management object instantiation

我不确定如何描述这个问题,但是这里有。我有一个在SQLite数据库中映射的对象的类层次结构。我已经编写了所有在.NET对象和数据库之间进行通信的非平凡代码。

我有一个基本界面如下:

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

这是对任何对象的基本CRUD操作。然后我实现了一个封装了大部分功能的基类。

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
}

现在,最后,我有了具体的子对象,每个对象在数据库中都有自己的表:

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

到目前为止,这对我的所有目的都有效。子进程有几个回调方法,基类使用这些方法来正确实例化数据。

我现在想要提高效率。例如,在以下代码中:

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();
}

在这种情况下,我将构建两个全新的内存实例化,并且根据被调用的SomeFunction1和SomeFunction2的顺序,可能无法保存Property1或Property2。我想要实现的是这两种实例化以某种方式指向相同内存位置的方式 - 如果我使用“new”关键字,我认为这不可能,所以我一直在寻找提示如何进行。

理想情况下,我想在我的ABackendObject类中存储所有已加载对象的缓存,并在请求时返回对已加载对象的内存引用,或者如果该对象尚不存在则从内存加载该对象并将其添加到缓存中。我已经有很多代码已经在使用这个框架了,所以我当然要改变很多东西来实现这个功能,但我只是想要一些关于如何继续的提示。

谢谢!

5 个答案:

答案 0 :(得分:6)

如果要存储已加载对象的“缓存”,您可以轻松地让每个类型都维护一个Dictionary<int, IBackendObject>来保存已加载的对象,并按其ID键入。

不使用构造函数,而是构建一个检查缓存的工厂方法:

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;
     }
} 

如果您使基类具有通用性,并且Read virtual,则应该能够提供大部分此功能而无需重复代码。

答案 1 :(得分:2)

你想要的是一个对象工厂。使ChildObject构造函数为private,然后编写一个返回ChildObject.Create(int index)的静态方法ChildObject,但它在内部确保具有相同索引的不同调用返回相同的对象。对于简单的情况,index =&gt;的简单静态哈希对象就足够了。

答案 2 :(得分:1)

如果您使用的是.NET Framework 4,您可能需要查看System.Runtime.Caching命名空间,它为您提供了非常强大的缓存架构。

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

答案 3 :(得分:0)

这样的参考计数听起来很完美......

    #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

答案 4 :(得分:0)

我认为你的正确轨道或多或少。您可以创建一个创建子对象的工厂(并可以跟踪“实时”实例),也可以跟踪已保存的实例,这样当您调用Save方法时,它会识别出您的第一个{{ 1}}与第二个ChildObject实例相同,并将第二个实例的数据深层复制到第一个实例。从编码的角度来看,这两者都是非常重要的,并且两者都可能涉及覆盖实体上的相等方法。我倾向于认为使用第一种方法不太可能导致错误。

另外一个选项是使用现有的Obect-Relational映射包(如NHibernateEntity Framework)来对象和数据库之间进行映射。我知道NHibernate支持Sqlite,根据我的经验,往往需要对实体结构进行最少量的更改。走这条路线,您可以获得ORM层跟踪实例的好处(并为您生成SQL),此外,您可能会获得当前数据访问代码可能没有的更高级功能。缺点是这些框架往往会有一个与之相关的学习曲线,并且根据您的具体情况,可能会对其余代码产生不可忽视的影响。因此,有必要权衡利用学习框架和转换代码以使用API​​的成本的好处。