使用部分从自动生成的 linq-sql 对象中抽象方法
本文关键字:linq-sql 对象 抽象方法 自动生成 用部 | 更新日期: 2023-09-27 18:26:05
背景:您好,我正在尝试构建像状态引擎这样的Windows工作流。我有一个基本的引擎设置了Action
和Trigger
- 操作执行自定义代码,触发器是允许状态引擎从一个状态移动到另一个状态的外部事件。 Trigger
持有许多Actions
,当Trigger
的bool isMet()
条件为真时触发。
我遇到的编码问题是我需要抽象Trigger
类的isMet()
方法。原因是我有很多子类Trigger
例如 从基Trigger
类继承的isPaperworkCompletedTrigger
,它们都包含自己的自定义isMet()
代码。我在实现这一点时遇到的唯一复杂情况是整个引擎,例如 Trigger
和 Action
需要存储在数据库中。我首先在 SQL 中生成引擎表,然后使用 LINQ-to-SQL 生成我的 Action
和Trigger
对象。LINQ-to-SQL 确实允许您使用 partial
类方法扩展自动生成的类对象,我已使用该方法将isMet()
方法添加到我的Trigger
类,我无法使此isMet()
方法抽象,因为自动生成的Trigger
类不是抽象的(出于显而易见的原因(。
我尝试通过继承子类中的基Trigger
类来"软覆盖"isMet()
方法,例如 isPaperworkCompletedTrigger
并创建一个名为isMet()
的方法,Intellisense对此有所抱怨,并告诉我停止Intellisense抱怨在方法上使用"new"关键字。正如预期的那样,这种"软覆盖"方法不起作用。
当Trigger
对象从数据库中提取出来并且自然调用isMet()
方法时,调用isMet()
方法的基本方法(来自Trigger
类,而不是子类(,这是有意义的,因为数据库无法知道Trigger
的哪个子项调用isMet()
方法。
显而易见的解决方案是在Triggers
表中粘贴一个TriggerName
字段,并在这个字段上做一个很好的旧开关案例,根据 name 字段是什么调用相应 Trigger
子类的 isMet()
方法。这是我想避免的。
我希望这个项目能够允许用户"插入"Trigger
和Action
。我计划完成此操作的方法是允许用户将自己的自定义Trigger
派生类作为 DLL 放入指定的文件夹中,并让工作流引擎能够在不重新部署或重建的情况下使用这些类(这排除了静态字符串上的大量开关大小写语句(。
这个问题的核心是研究如何读取所有Trigger
模块(一个DLL是一个Trigger
模块(,并在此对象上调用isMet()
方法(无权访问其类代码(。
我怀疑解决这个问题的攻击点在于使Trigger
类isMet()
方法抽象或放置某种转换器类以从数据库Trigger
类转换为"离线"Trigger
类并使该离线类抽象(我可以从中覆盖(。
任何人都可以帮助解决这个问题。
非常抱歉我的小说长度问题,但这个问题确实需要相当多的信息才能让任何人理解这个问题。
谢谢
与其在类abstract
的基础上创建isMet()
方法Trigger
,不如让它virtual
,默认值可能为 false。 然后,可以使用 override
关键字在派生类中override
该方法。
第二个问题涉及将触发器序列化和反序列化到数据库。 反序列化时,需要确保获取派生的触发器类型,而不是基触发器类型。 我不知道您是如何选择将对象序列化为 databse 的,但您需要一种方法来存储该类型。 让我们以DataContractSerializer
为例。 它接受Type
作为第一个参数。 如果在序列化触发器时将 typeof(派生触发器(存储到数据库中的另一个字段,则可以反序列化 Type,并使用它来将触发器反序列化为正确的派生类型。 然后调用 isMet()
方法应调用派生的重写值。 下面是一个使用静态变量代替数据库的简短示例:
[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
,是因为,就像你说的,在 DataContractSerializer 中对类型使用 typeof(Trigger)
不会反序列化实际上是派生触发器的对象。 因此,您需要将派生类型与对象一起存储在数据库中。
还没有说你正在使用什么 dbms,但我会使用 blob 来表示触发器列,并使用 varbinary 或 blob 来表示serializedObjectType
列,即触发器的实际派生类型。 使用硬编码的类型序列化程序序列化类型。即DataContractSerializer(typeof(Type), ...)
并使用DataContractSerializer(typeof(T), ...)
序列化对象,其中 T 是派生的触发器类型,您可以使用泛型类型变量获取该类型。
反序列化时,请反向执行。 首先使用硬编码类型序列化程序反序列化类型。即DataContractSerializer(typeof(Type), ...)
,然后使用反序列化类型的结果反序列化对象。 我已经更新了我的代码片段,希望能更好地说明我提出的策略。 很抱歉我的回复迟到了。
编辑 2
通常,在谈论序列化时,您只序列化对象中的值,因为这是将一个对象与另一个对象分开的原因。 不需要将方法主体序列化为数据库,因为它存储在文件系统(您在问题中提到的可插入 dll(的程序集中。 加载这些 dll 是一个单独的步骤。 看看System.Reflection.Assembly.LoadFile()
.
从您问题中的这两个陈述中:
触发器和操作需要存储在数据库中。
和
。用户将自己的自定义 Trigger 派生类作为 DLL 放入指定的文件夹中。
我假设(也许是错误的(类的定义将存储在 fs 上,而进入每个类对象的数据将存储在数据库中。 如果派生的 isMet()
方法是静态的(可能不是显式的,但没有任何关联的状态(,则数据库中不会存储任何内容。 但是,听起来您正在设置它,以便Trigger
包含Actions
集合。在这种情况下,这些Actions
就是序列化到数据库的内容。 如果您的类Serializable
,只需将它们标记为公共,或者直接将集合标记为 Serializable
. 然后,内存流的大小将与每个触发器保持的Actions
数成正比。 清澈如泥?
你只是想在 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 字符串,然后在文件夹中查找程序集,加载具有匹配名称的类并找到适当的类并使用反射在类上调用 IsMet 方法,然后返回结果......等。。。
edit3:或者,使用不同的ORM而不是linq-to-sql。NHibernate有可能进行动态继承。例如,看到这个问题
edit4:事实上,这里有人写了关于使用NHibernate做你正在尝试做的事情的文章:"使用 C# 和 NHibernate 创建动态状态机">
http://blog.lowendahl.net/?p=164