Подтвердить что ты не робот

Сохранение шаблона состояния с использованием Entity Framework

В настоящее время я разрабатываю проект в MVC 3. Я отделил свои проблемы, поэтому есть такие проекты, как Core, Repository, UI, Services и т.д. Я реализую репозиторий, UnitOfWork и, самое главное, шаблон состояния.

Я использую Entity Framework 4.3 для сохранения своих данных, и я столкнулся с довольно неприятной ситуацией, связанной с сохранением текущего состояния. Ниже приведены некоторые примеры классов:

public class Request
{
    public int RequestId { get; set; }

    public State CurrentState { get; set; }
}

public abstract class State
{
    [Key]
    public string Name {get; set;}

    public virtual void OpenRequest(Request request)
    {}

    public virtual void CloseRequest(Request request)
    {}
}

public class RequestIsOpenState : State
{
    public RequestIsOpenState()
    {
        this.Name = "Open";
    }

    public override void CloseRequest(Request request)
    {
        request.CurrentState = new RequstIsClosedState();
    }
}

public class RequestIsClosedState : State
{
    public RequestIsClosedState()
    {
        this.Name = "Closed";
    }

    public override void OpenRequest(Request request)
    {
        request.CurrentState = new RequstIsOpenState();
    }
}

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

Поскольку изменение состояния выполняется на уровне домена, я не могу просто "получить" состояние из репозитория и установить его с помощью внешнего ключа, выполнив что-то вроде этого:

Request request = unitOfWork.RequestRepository.Find(1);
request.CurrentState = unitOfWork.StateRepository.Find("Closed");

Я знаю, что у меня есть возможность не сопоставлять свойство состояния и сохранять свойство строки в классе запроса, а затем преобразовывать их обратно и вперед через factory в get и задавать, когда объект гидратируется ( см. этот ответ).

Все, что я хочу сделать, это сохранить класс состояния, поэтому, когда запрос будет возвращен, я могу немедленно получить доступ к методам состояния, не загружая элементы EF, загрязняющие мой доменный уровень, чтобы справиться с одной проблемой сохранения. Другим преимуществом этого является то, что это дает мне дополнительный бонус наличия таблицы в SQL для запроса от известных состояний.

4b9b3361

Ответ 1

Я сделал некоторый прогресс, упростив factory к основам и внедряя его таким образом, чтобы вы никогда не знали, что используется factory. Хотя это не то, что я искал, он настолько утончен и оптимизирован, что единственный недостаток у меня до сих пор не имеет списка ВСЕХ состояний в базе данных SQL, однако для этого существует множество возможных проблем. Во всяком случае... мой компромисс:

Состояние Factory:

public static State GetState(string stateTypeName)
{
    var list = FindAllDerivedStates();
    dynamic returnedValue = new NullState();
    foreach(var state in list)
    {
        if(state.Name == stateTypeName) returnedValue = (State)Activator.CreateInstance(state);
    }
    return returnedValue
}

private static List<Type> FindAllDerivedStates()
{
    var derivedType = typeof(State);
    var assembly = Assembly.GetAssembly(typeof(State));
    return assembly.GetTypes().Where(t => t != derivedType && derivedType.IsAssignableFrom(t)).ToList();
}

Теперь для запроса требуются два свойства: постоянная строка и класс состояний. Убедитесь, что класс State не отображен.

public class Request
{
    public string StateString { get; set; }

    [NotMapped] or [Ignore]
    public State CurrentState 
    { 
        get
        {
            return StateFactory.GetState(this.StateString); 
        }
        set
        { 
            this.State = value.GetType().Name; 
        }
    }
}

Теперь из-за новой упрощенной реализации сохранение состояния так же просто, как:

request.CurrentState = new OpenState();

и получение состояния всегда будет возвращать методы. Без какой-либо дополнительной работы вы можете вернуть объект и превысить свойства. Например, если вы хотите вывести общедоступную строку,

request.CurrentState.StateName;

Теперь мне еще нужно немного поработать, чтобы добавить список состояний в мой SqlDb, но это не конец света. Кажется, это единственное решение. Или я должен сказать лучшее решение. Я буду держать глаза очищенными для лучшей версии.

Ответ 2

Я думаю, что вы можете улучшить его, кэшируя экземпляры State, создавая его только один раз, чтобы избежать создания списка каждый раз и избегать foreach:

public static class StateFactory
{
    private static Dictionary<string, State> statesCache = FindAllDerivedStates();

    public static State GetState(string stateTypeName)
    {
        return statesCache[stateTypeName];
    }

    private static Dictionary<string, State> FindAllDerivedStates()
    {
        var derivedType = typeof(State);
        var assembly = Assembly.GetAssembly(typeof(State));
        return assembly.GetTypes().Where(t => t != derivedType && derivedType.IsAssignableFrom(t))
                    .Select(t => (State)Activator.CreateInstance(t))
                    .ToDictionary(k => k.Name);
    }
}