Инициализация конструктора из сохраненного кэша в C # - PullRequest
1 голос
/ 25 июня 2010

Я не уверен, как именно описать этот вопрос, но здесь идет.У меня есть иерархия классов объектов, которые отображаются в базе данных 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 ]

6 голосов
/ 25 июня 2010

Если вы хотите сохранить «кэш» загруженных объектов, вы можете просто заставить каждый тип поддерживать Dictionary<int, IBackendObject>, который содержит загруженные объекты, идентифицируемые по их идентификатору.

Вместо использования конструктора,создайте метод фабрики, который проверяет кэш:

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

Если вы сделаете свой базовый класс универсальным, а Чтение виртуальным, вы сможете обеспечить большую часть этой функциональности без большого дублирования кода.

2 голосов
/ 25 июня 2010

То, что вы хотите, это объект фабрики.Сделайте конструктор ChildObject закрытым, затем напишите статический метод ChildObject.Create(int index), который возвращает ChildObject, но который внутренне гарантирует, что различные вызовы с одинаковым индексом возвращают один и тот же объект.Для простых случаев будет достаточно простого статического хэша index => object.

1 голос
/ 25 июня 2010

Если вы используете .NET Framework 4, вы можете взглянуть на пространство имен System.Runtime.Caching, которое дает вам довольно мощную архитектуру кэширования.

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

0 голосов
/ 25 июня 2010

Я думаю, что вы на правильном пути более или менее.Вы можете создать фабрику, которая создает ваши дочерние объекты (и может отслеживать «живые» экземпляры), или вы можете отслеживать экземпляры, которые были сохранены, чтобы при вызове метода Save он распознавал, что ваш первый экземпляр ChildObject такой же, как ваш второй экземпляр ChildObject, и выполняет глубокое копирование данных из второго экземпляра в первый.Оба они довольно нетривиальны с точки зрения кодирования, и оба, вероятно, включают в себя переопределение методов равенства в ваших сущностях.Я склонен думать, что использование первого подхода с меньшей вероятностью приведет к ошибкам.

Еще одна дополнительная опция - использование существующего пакета реляционного сопоставления объектов, такого как NHibernate или EntityFramework для сопоставления объектов и базы данных.Я знаю, что NHibernate поддерживает Sqlite и, по моему опыту, имеет тенденцию быть той, которая требует наименьшего количества изменений в ваших структурах сущностей.Пройдя по этому маршруту, вы получите преимущество от отслеживания экземпляров слоя ORM для вас (и создания для вас SQL), плюс вы, вероятно, получите некоторые более продвинутые функции, которых может не иметь ваш текущий код доступа к данным.Недостатком является то, что эти фреймворки, как правило, имеют связанную с ними кривую обучения, и в зависимости от того, с чем вы идете, это может оказать незначительное влияние на остальную часть вашего кода.Так что стоило бы сравнить преимущества с затратами на изучение фреймворка и преобразование кода для использования API.

0 голосов
/ 25 июня 2010

Звучит идеально для подсчета ссылок вроде этого ...

    #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
...