Сохранение шаблона состояния с использованием 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 для запроса от известных состояний.
Ответы
Ответ 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);
}
}