后台:您好我正在尝试构建像州引擎这样的Windows工作流程。我有一个基本的引擎设置Action
和Trigger
- 动作做自定义代码,触发器是外部事件,允许状态引擎从一个状态移动到另一个状态。当Trigger
的{{1}}条件成立时,Actions
会保留许多Trigger
'。
我遇到的编码问题是我需要抽象bool isMet()
类的isMet()
方法。这样做的原因是我有很多次级Trigger
类,例如Trigger
继承自基础isPaperworkCompletedTrigger
类,并且每个类都包含自己的自定义Trigger
代码。我实现这一点的唯一复杂因素是整个引擎,例如: isMet()
和Trigger
需要存储在数据库中。我首先在SQL中构建引擎表,然后使用LINQ-to-SQL构建我的Action
和Action
对象。 LINQ-to-SQL允许您使用我用于向Trigger
类添加partial
方法的isMet()
类方法扩展自动生成的类对象,我无法制作这个Trigger
方法摘要,因为自动生成的isMet()
类不是抽象的(出于显而易见的原因)。
我尝试通过继承子类中的基类Trigger
类来“软覆盖”isMet()
方法,例如Trigger
并创建了一个名为isPaperworkCompletedTrigger
的方法,intellisense抱怨这一点,并告诉我停止intellisense抱怨在方法上使用'new'关键字。正如预期的那样,这种“软覆盖”方法不起作用。
当从isMet()
对象中拉出数据库并自然调用Trigger
方法时,将调用基本方法isMet()
方法(来自isMet()
类,并且不是子类),这是有道理的,因为数据库无法知道Trigger
的哪个孩子调用Trigger
方法。
明显的解决方案是在isMet()
表中粘贴TriggerName
字段,并在此字段上执行一个好的旧开关案例,调用相应子字段的Triggers
方法-class isMet()
基于名称字段的内容。这是我想避免的。
我希望这个项目能够让用户“插入”Trigger
和Trigger
。我计划实现这一目标的方法是允许用户将自己的自定义Action
派生类作为DLL放入指定的文件夹中,并让工作流引擎能够在不重新部署或重建的情况下使用它们(规则在静态字符串上输出大量的switch case语句。
这个问题的核心是如何读取所有Trigger
模块(一个DLL是一个Trigger
模块),并在此对象上调用Trigger
方法(没有有权访问其类代码。)
我怀疑解决这个问题的攻击点在于使isMet()
类Trigger
方法抽象或将某种转换器类转换为数据库isMet()
类到'离线'Trigger
类并使该离线类抽象(我可以覆盖它)。
任何人都可以帮助解决这个问题。
对于我的小说延长问题非常抱歉,但问题确实需要大量信息才能让任何人理解这个问题。
由于
答案 0 :(得分:2)
不要在基础isMet()
类Trigger
上设置abstract
方法,而是将其设为virtual
,默认值为false。然后,您可以使用override
关键字override
在派生类中使用{{3}}方法。
您的第二个问题涉及序列化和反序列化数据库的触发器。反序列化时,您希望确保获得派生的触发器类型,而不是基础。我不知道你是如何选择将对象序列化为数据库的,但是你需要一种方法来存储类型。让我们以DataContractSerializer
为例。它接收Type
作为它的第一个参数。如果在序列化触发器时将typeof(DerivedTrigger)存储到数据库中的另一个字段,则可以反序列化Type并使用它将Trigger反序列化为正确的派生类型。然后调用isMet()
方法应该调用派生的overriden值。以下是使用静态变量代替数据库的简短示例:
[DataContract]
partial class Trigger
{
public virtual bool isMet()
{
return false;
}
}
[DataContract]
class DerivedTrigger : Trigger
{
public object DataElement1 { get; set; }
//and other properties to serialize.
public override bool isMet()
{
return true;
}
}
void Main()
{
DerivedTrigger t = new DerivedTrigger();
Serialize(t);
((Trigger)Deserialize()).isMet(); // returns True!
}
public static void Serialize<T>(T source)
{
MemoryStream ms = new MemoryStream();
Type serializedObjectType = typeof(T);
DataContractSerializer dcsObject = new DataContractSerializer(serializedObjectType, null, int.MaxValue, false, true, null);
dcsObject.WriteObject(ms, source); //serialize the object
byte[] buffer = new byte[1024] //TODO: adjust size
ms.Position = 0;
ms.Read(buffer, 0, 1024);
//TODO: write buffer to database colObject here
ms.Position = 0;
DataContractSerializer dcsType = new DataContractSerializer(typeof(Type), null, int.MaxValue, false, true, null);
dcsType.WriteObject(ms, serializedObjectType.DeclaringType);
buffer = new byte[1024]
ms.Position = 0;
ms.Read(buffer, 0, 1024);
//TODO: write buffer to database colType here
}
public static object Deserialize()
{
MemoryStream ms = new MemoryStream();
byte[] buffer = new byte[1024];
//TODO: read colType into buffer here
ms.Write(buffer, 0 1024);
ms.Position = 0;
DataContractSerializer dcsType = new DataContractSerializer(typeof(Type), null, int.MaxValue, false, true, null);
Type serializedObjectType = dcs.Read(ms);
//TODO: read colObject into buffer here
DataContractSerializer dcs = new DataContractSerializer(serializedObjectType, null, int.MaxValue, false, true, null);
return dcs.ReadObject(serializedObject);
}
修改强>
好吧,使用MemoryStream似乎已经混淆了这种情况。 MemoryStream不是存储在数据库中的东西,它是数据库。
拥有serializedObjectType
的全部原因是因为,就像你说的那样,使用typeof(Trigger)
作为DataContractSerializer中的类型将不会反序列化实际派生触发器的对象。因此,您需要将派生类型与对象一起存储在数据库中。
您还没有说过您正在使用的dbms,但我会使用blob来表示Trigger列,使用varbinary或blob来表示serializedObjectType
列,即实际派生类型触发。使用硬编码类型序列化器序列化类型。即DataContractSerializer(typeof(Type), ...)
并使用DataContractSerializer(typeof(T), ...)
序列化对象,其中T是派生触发器类型,您可以使用泛型类型变量获取该类型。
反序列化时,反向执行。首先使用硬编码类型序列化器对类型进行反序列化。 ie DataContractSerializer(typeof(Type), ...)
然后使用反序列化类型的结果反序列化对象。我已经更新了我的代码片段,希望能更好地说明我提出的策略。很抱歉我的回复很晚。
编辑2
通常,在谈论序列化时,只序列化对象中的值,因为这是将一个对象与另一个对象分开的原因。您不需要将方法体序列化到数据库,因为它存储在文件系统的程序集中(您在问题中提到的可插入的dll)。这些dll的加载是一个单独的步骤。看看System.Reflection.Assembly.LoadFile()
。
你问题中的这两个陈述:
需要将触发器和操作存储在数据库中。
和
...用户将自己的自定义Trigger派生类作为DLL放入指定的文件夹中。
我假设(可能不正确)类的定义将存储在fs中,并且进入每个类的对象的数据将存储在数据库中。如果派生的isMet()
方法是静态的(可能不是显式的,但没有任何关联状态),那么数据库中就不会存储任何内容。但是,听起来你正在设置它,以便Trigger
拥有Actions
的集合。在这种情况下,那些Actions
被序列化到数据库。如果您的课程为Serializable
,请将其标记为公开,或者将该集合直接标记为Serializable
。然后,存储器流的大小将与每个触发器保持的Actions
的数量成比例。像泥一样清楚?
答案 1 :(得分:0)
你只是想在Linq-to-Sql中做继承,对吧?
这并不罕见。您需要一个带有IsMet方法的基类,然后使用适当的逻辑覆盖它的子类。
诀窍是,让Linq-to-Sql在获取数据时实例化正确的子类。
我相信可以用这个来完成:
如何:映射继承层次结构(LINQ to SQL)
http://msdn.microsoft.com/en-us/library/bb399352.aspx
编辑:好的,也许这不起作用,因为您需要提前知道所有类以填写[InheritanceMapping]
属性。所以你要求的是Linq-to-SQL中的动态继承,其中编译器事先不知道子类将是什么。我不确定那是否可能。
edit2:为了动态地做你要求的,我不认为Linq-to-sql-inheritance会削减它。或部分方法。也许反射是你最好的选择。那就是:一个主要的Trigger类,带有一个IsMet()方法的怪物,它读取一个TriggerType字符串,然后在一个文件夹中查找Assemblies,加载一个具有匹配名称的那个,找到合适的类并在类上调用一个IsMet方法使用反射,然后返回结果...等...
edit3:或者,使用不同的ORM而不是linq-to-sql。 NHibernate有可能进行动态继承。例如。请参阅此question
edit4:事实上,这里有人在写关于使用NHibernate来做你想要做的事情:
“使用C#和NHibernate创建动态状态机”
http://blog.lowendahl.net/?p=164