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

时间:2012-02-27 11:43:21

标签: c# .net linq-to-sql inheritance abstract-class

后台:您好我正在尝试构建像州引擎这样的Windows工作流程。我有一个基本的引擎设置ActionTrigger - 动作做自定义代码,触发器是外部事件,允许状态引擎从一个状态移动到另一个状态。当Trigger的{​​{1}}条件成立时,Actions会保留许多Trigger'。

我遇到的编码问题是我需要抽象bool isMet()类的isMet()方法。这样做的原因是我有很多次级Trigger类,例如Trigger继承自基础isPaperworkCompletedTrigger类,并且每个类都包含自己的自定义Trigger代码。我实现这一点的唯一复杂因素是整个引擎,例如: isMet()Trigger需要存储在数据库中。我首先在SQL中构建引擎表,然后使用LINQ-to-SQL构建我的ActionAction对象。 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()基于名称字段的内容。这是我想避免的。

我希望这个项目能够让用户“插入”TriggerTrigger。我计划实现这一目标的方法是允许用户将自己的自定义Action派生类作为DLL放入指定的文件夹中,并让工作流引擎能够在不重新部署或重建的情况下使用它们(规则在静态字符串上输出大量的switch case语句。

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

我怀疑解决这个问题的攻击点在于使isMet()Trigger方法抽象或将某种转换器类转换为数据库isMet()类到'离线'Trigger类并使该离线类抽象(我可以覆盖它)。

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

对于我的小说延长问题非常抱歉,但问题确实需要大量信息才能让任何人理解这个问题。

由于

2 个答案:

答案 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