我不确定如何描述这个问题,但是这里有。我有一个在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
类中存储所有已加载对象的缓存,并在请求时返回对已加载对象的内存引用,或者如果该对象尚不存在则从内存加载该对象并将其添加到缓存中。我已经有很多代码已经在使用这个框架了,所以我当然要改变很多东西来实现这个功能,但我只是想要一些关于如何继续的提示。
谢谢!
答案 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映射包(如NHibernate或Entity Framework)来对象和数据库之间进行映射。我知道NHibernate支持Sqlite,根据我的经验,往往需要对实体结构进行最少量的更改。走这条路线,您可以获得ORM层跟踪实例的好处(并为您生成SQL),此外,您可能会获得当前数据访问代码可能没有的更高级功能。缺点是这些框架往往会有一个与之相关的学习曲线,并且根据您的具体情况,可能会对其余代码产生不可忽视的影响。因此,有必要权衡利用学习框架和转换代码以使用API的成本的好处。