使用Entity Framework保持状态模式

Heb*_*rda 15 c# state entity-framework unit-of-work ef-code-first

我目前正在开发MVC 3中的项目.我已将我的问题分开,因此有一些项目,如Core,Repository,UI,Services等.我实现了Repository,UnitOfWork,最重要的是State模式.

我正在使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

使用上面的示例,我将获得主键冲突异常,因为它尝试在States表中创建一个NEW状态.

因为状态更改是在域层内完成的,所以我不能只是从存储库"获取"状态并使用外键设置它,方法如下:

Request request = unitOfWork.RequestRepository.Find(1);
request.CurrentState = unitOfWork.StateRepository.Find("Closed");
Run Code Online (Sandbox Code Playgroud)

我知道我可以选择不映射state属性,并在请求类中保留一个字符串属性,然后在实体被水合时在get和set中通过工厂来回转换它们(请参阅此答案).

我想做的就是保持状态类,所以当返回请求时,我可以立即访问状态方法,而不会有大量的EF东西污染我的域层只是为了处理一个持久性问题.另一个好处是它给了我额外的好处,在SQL中有一个表来查询已知状态.

the*_*000 5

我认为您可以通过仅缓存创建它一次的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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Heb*_*rda 2

我通过将工厂简化回到基础并以一种你永远不会真正知道正在使用工厂的方式实现它,取得了一些进展。虽然这不是我想要的,但它是如此精致和精简,唯一的缺点是我仍然没有数据库中所有状态的列表SQL,但是有很多可能的解决方法。无论如何......我的妥协:

国营工厂:

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();
}
Run Code Online (Sandbox Code Playgroud)

现在请求需要两个属性,一个持久字符串和一个 State 类。确保 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; 
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,由于新的简单实现,保存状态就像以下一样简单:

request.CurrentState = new OpenState();
Run Code Online (Sandbox Code Playgroud)

获取状态将始终返回方法。无需任何额外的工作,您就可以返回一个实体并超出其属性。例如,如果你想输出公共字符串;

request.CurrentState.StateName;
Run Code Online (Sandbox Code Playgroud)

现在我仍然需要做一些工作来将状态列表添加到我的 SqlDb 中,但这并不是世界末日。看来这是唯一的解决办法了。或者我应该说最好的解决方案。我会密切关注以获得更好的版本。