我是状态设计模式的新手,我找不到将对象的不同状态保存到数据库的正确示例(在我的情况下是SQL Server)。该场景与下一篇文章中描述的示例非常相似[几乎相同],但是我没有找到将状态持久保存到数据库的适用解决方案。你们可以推荐一个链接或者可能给出一个例子吗?
State Pattern Usage and Sample in C#
另外:如何在运行时枚举所有不同的ConcreteState类型?例如,如果你有10个不同的状态,你是否声明一个包含10个不同成员的EnumStates并为每个ConcreteState成员提供一个关联的EnumStates成员,或者你通过获取ConcreteState的子类来获得所有不同的状态?
对于您的信息,我需要能够根据不同的状态搜索实体。
答案 0 :(得分:7)
国家实体本身没有国家,所以你需要保存的只是每个国家的身份。将State类名保存在数据库中并不是一个好主意,因为如果更改了State类名,则必须更改数据库。取而代之的是,
要在加载对象时返回状态,
无论哪种方式,您都需要能够从Enum值转到State。通过循环遍历所有相关的State类来执行此操作,直到找到其标识值匹配的类。
那么,相关国家是什么?这取决于谁在写国家班。
在简单的情况下,您控制整个程序并且程序中的所有State类都可能是State-having对象的成员,您可以遍历State超类的所有子类或实现者或界面,如下所示:Getting all types that implement an interface。
如果由于某种原因存在您不想循环的状态类,只需定义一个您想要在常量中循环的列表或(如果您想要更改它而没有在配置中更改代码。
如果使状态类列表很慢,只需在程序启动或首次使用时执行一次。如果您对列表进行硬编码,请不要在具有State的类中(它应该独立于特定的状态)或State超类(这会引入循环依赖);将列表放在你的程序中更高(依赖性)或(如Farhad建议的那样)在自己的类中。
There are a lot of examples how how to persist objects with State out there; this one相对简单。
答案 1 :(得分:4)
不要试图将状态转换为表中的列,这些列不起作用。
而是使用JSON.NET序列化状态,因为它支持继承。然后将其存储在如下表格中:
create table OrderStates
(
OrderId int not null,
Data nvarchar(MAX) not null
);
如果需要,请包含更多列,但只包含识别状态所用的列。
要激活JSON.NET中的继承支持,您必须使用:
var json = JsonConvert.SerializeObject(yourState, typeof(StateBaseClass), JsonConvert.DefaultSettings)`.
using (var cmd = sqlConnection.CreateCommand())
{
cmd.CommandText = "INSERT INTO OrderStates (OrderId, Data) VALUES(@OrderId, @Data)";
cmd.Parameters.AddWithValue("OrderId", orderId);
cmd.Parameters.AddWithValue("Data", json);
cmd.ExecuteNonQuery();
}
反序列化时也一样,在使用JsonConvert.DeserializeObject()
时指定基类。
如何在运行时枚举所有不同的ConcreteState类型?例如,如果你有10个不同的状态,你是否声明一个包含10个不同成员的EnumStates并为每个ConcreteState成员提供一个关联的EnumStates成员,或者你通过获取ConcreteState的子类来获得所有不同的状态?
子类。这是能够引入新状态或删除旧状态而无需修改其他类的唯一方法。现有类的每次修改都可能引入错误。
答案 2 :(得分:3)
我不喜欢你链接的例子,下面列出了原因:
我会选择这种模式:
模式基础架构
public interface IStateObject<T>
{
T State { get; set; }
void Process();
}
某些伪订单对象的示例实现
public enum OrderState
{
Taken,
Approved,
Payed,
Emailed,
BeforeShipment
//etc.. etc..
}
public class Order : IStateObject<OrderStates>
{
//some linear fields of order..
//: name, description, etc.. etc..
public OrderStates State { get; set; }
public void Process()
{
switch (State)
{
case OrderState.Taken:
// code to handle this state
break;
case OrderState.Approved:
// etc..
break;
}
//persist myself to db.
}
}
这非常简单,因为您可以在一行中按对象的每个上下文保存对象。
如果我们没有计算机接近我们,也应该直观地创建一个对象。
但主要是因为它非常直接且非常灵活。
您可能会注意到实际上可能根本不需要IStateObject<T>
- 但我认为您以后需要处理垂直决策时需要它。
请记住,T
不一定是枚举。它可以作为根据您的应用需求发展的共同基础。
进一步指出我在答案开头提到的混乱,
假设我们希望将历史记录到订单的先前状态:
使用此答案中提供的模式 - 您添加了PreviousOrderState属性,现在每行都有历史记录..还有其他方法我确定您可以想到..
但是使用“状态模式” - 你将遇到严重的问题......它实际上会因为完整的“比例级别”而变得复杂......你必须能够从每种类型链接table对于每种类型的其他表 - 或者尝试在数据库上强制面向对象...
看到我的观点? States模式根本不适用于以数据为中心的应用程序。
祝你好运。
答案 3 :(得分:1)
我同意Dave的回答,直到你需要在加载对象时获得状态。我不认为当你有许多具有状态的不同对象时,总是遍历所有状态类甚至类列表都是个好主意。
我认为在这种情况下应该有一个StateManager
类,它还可以包含状态枚举定义以及每个Enum
值与其State
对象之间的映射({{1 }})。此映射应该是硬编码的,或者从配置文件中读取。这个类可以在第一次访问状态时处理状态的延迟加载。如果州不具有字段而非功能性(如OP提交中的示例链接),它也可以将它们创建为Singleton个对象。
答案 4 :(得分:0)
状态模式可用于以数据为中心的系统,例如处理工作流程订单和批准业务的Web应用程序。状态操纵和持久存储发生在状态切换逻辑的不同时期。例如,委托对象将负责状态切换,而状态切换事件发生时应进行数据库操作。您还可以在状态机对象中预定义所有业务状态流。当状态更改事件发生时,触发状态机以查找其是否在预定义流中。可以在https://github.com/elimisteve/fsm
中找到一个小演示