使用部分从自动生成的 linq-sql 对象中抽象方法

本文关键字:linq-sql 对象 抽象方法 自动生成 用部 | 更新日期: 2023-09-27 18:26:05

背景:您好,我正在尝试构建像状态引擎这样的Windows工作流。我有一个基本的引擎设置了ActionTrigger - 操作执行自定义代码,触发器是允许状态引擎从一个状态移动到另一个状态的外部事件。 Trigger持有许多Actions,当Triggerbool isMet()条件为真时触发。

我遇到的编码问题是我需要抽象Trigger类的isMet()方法。原因是我有很多子类Trigger例如 从基Trigger类继承的isPaperworkCompletedTrigger,它们都包含自己的自定义isMet()代码。我在实现这一点时遇到的唯一复杂情况是整个引擎,例如 TriggerAction 需要存储在数据库中。我首先在 SQL 中生成引擎表,然后使用 LINQ-to-SQL 生成我的 ActionTrigger对象。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() 方法。这是我想避免的。

我希望这个项目能够允许用户"插入"TriggerAction。我计划完成此操作的方法是允许用户将自己的自定义Trigger派生类作为 DLL 放入指定的文件夹中,并让工作流引擎能够在不重新部署或重建的情况下使用这些类(这排除了静态字符串上的大量开关大小写语句(。

这个问题的核心是研究如何读取所有Trigger模块(一个DLL是一个Trigger模块(,并在此对象上调用isMet()方法(无权访问其类代码(。

我怀疑解决这个问题的攻击点在于使TriggerisMet()方法抽象或放置某种转换器类以从数据库Trigger类转换为"离线"Trigger类并使该离线类抽象(我可以从中覆盖(。

任何人都可以帮助解决这个问题。

非常抱歉我的小说长度问题,但这个问题确实需要相当多的信息才能让任何人理解这个问题。

谢谢

使用部分从自动生成的 linq-sql 对象中抽象方法

与其在类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