Annotation methods from automatically generated linq-sql objects using partial

Background: Hello, I'm trying to create a Windows workflow, for example, using the state mechanism. I have a basic engine configured using Action and Trigger - Actions execute their own code, triggers are external events that allow the state engine to move from one state to another. Trigger hold a lot of Actions ' that fire when the Trigger bool isMet() condition is true.

The problem with the coding that I am having is the need for the abstract isMet() method of the Trigger class. The reason for this is that I have many sub Trigger classes, for example. isPaperworkCompletedTrigger , which inherit from the Trigger base class, and each of them contains its own isMet() code. The only complication that I experience when implementing this is that the entire engine, for example, Trigger and Action must be stored in the database. First, I built machine tables in SQL, and then used LINQ-to-SQL to create Action and Trigger objects. LINQ-to-SQL allows you to extend automatically generated class objects using the partial class method that I used to add the isMet() method to my Trigger class, I cannot make this isMet() abstract method because the automatically generated Trigger class is not abstract (for obvious reasons).

I tried to "gently override" the isMet() method, inheriting the Trigger base class in my subclasses, for example. isPaperworkCompletedTrigger and by creating a method called isMet() , intellisense complains a bit about this and tells me to stop intellisense from complaining in order to use the "new" keyword in the method. As expected, this soft redefinition method does not work.

When Trigger objects are pulled out of the database, and the isMet() method is called, of course, the base method isMet() from the Trigger class, not a subclass), this makes sense because the database has no way of knowing which Trigger child calls the isMet() method.

The obvious solution for this is to insert the TriggerName field into the Triggers table and make a good old switch case in this field by calling the isMet() method of the corresponding Trigger subclass based on the name field. I want to avoid that.

I would like this project to allow users to "connect" Trigger and Action . The way I plan to do this is to allow the user to discard their own custom Trigger derived classes as DLLs in the specified folder and have a workflow mechanism that can use them without redeploying or rebuilding (which eliminates massive statements about switch cases for static strings).

The basis of this problem is how to read in all Trigger modules (one DLL is one Trigger module) and call the isMet() method on this object (without access to its code class).

I suspect that the attack point to solve this problem is to create an abstract Trigger method class isMet() abstract OR isMet() some converter class to convert from the Trigger database to the 'offline' Trigger and make this a stand-alone abstract class (which I can override )

Does anyone help in solving this problem.

I am very sorry for my new question, but the problem requires a lot of information so that someone can understand this question.

thanks

+4
source share
2 answers

Instead of using the Trigger class abstract based isMet() method, make virtual , perhaps the default value is false. You can then override method in derived classes using the override keyword.

The second problem is related to the serialization and deserialization of triggers in the database. When you deserialize, you want to make sure that you are returning a derived trigger type, not the base. I don’t know how you chose to serialize your objects into a database, but you need a way to save this type. Take a DataContractSerializer , for example. As the first parameter, it takes Type . If you store typeof (DerivedTrigger) in a different field in your database when triggers are serialized, you can deserialize the type and use it to deserialize the trigger to the correct derived type. Then calling your isMet() method should call the derived override value. The following is a brief example of using static variables instead of a database:

 [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); } 

EDIT

Well, using a MemoryStream seems to have confused the situation. MemoryStream is not what is stored in the database, it is a database.

The whole reason for having serializedObjectType is because, as you say, using typeof(Trigger) for a type in a DataContractSerializer will not deserialize objects that are actually derived triggers. Therefore, you need to save the derived type with the object in the database.

You did not say which dbms you are using, but I would use blob to represent the Trigger column and either varbinary or blob to represent the serializedObjectType column, i.e. the actual most derived Trigger type. Serialize the type using a solid-state type encoder. those. DataContractSerializer(typeof(Type), ...) and serialize the object using DataContractSerializer(typeof(T), ...) , where T is the derived trigger type that you can get with a generic variable.

When deserializing, do this in reverse order. First, we deserialize the type using the solid-state type serializer. those. DataContractSerializer(typeof(Type), ...) , and then deserialize the object with the results of the deserialized type. I updated my code snippets, I hope it is better to illustrate your strategy. Sorry for the delay in my reply.

EDIT 2

Typically, when you talk about serialization, you only serialize the values ​​in the object, since this is what separates one object from another. You do not need to serialize the method body in the database, since it is stored in assemblies in the file system (pluggable DLL files that you specify in your question). Downloading these DLLs is a separate step. take a look at System.Reflection.Assembly.LoadFile() .

Of these two statements in your question:

The trigger and action must be stored in the database.

and

... the user must discard their own trigger classes as DLLs in the specified folder.

I assumed (possibly incorrectly) that the definition for the class will be stored in fs, and the data that goes into each class object will be stored in the database. If your derived isMet() methods are static (perhaps not explicitly, but do not have any associated state), then nothing will be stored in the database. However, it looks like you are setting it up so that Trigger stores the Actions collection. In this case, those Actions are what are serialized in the database. Just mark them public if your class is Serializable , or mark the collection directly as Serializable . Then the size of the memory stream will be proportional to the number of Actions each trigger. Clean like dirt?

+2
source

You are just trying to do inheritance in Linq-to-Sql, right?

This is not unusual. You need a base class with the IsMet method, and then subclasses that override it using the appropriate logic.

The trick is getting Linq-to-Sql to create the right subclass when it retrieves data.

I believe this can be done with this:

A practical guide. Map Inheritance Hierarchies (LINQ to SQL)
http://msdn.microsoft.com/en-us/library/bb399352.aspx

edit: Okay, maybe this won't work, because you need to know all the classes in advance to populate the [InheritanceMapping] attributes. So, you are requesting dynamic inheritance in Linq-to-SQL, where the compiler does not know in advance what subclasses will be. I am not sure if this is possible.

edit2: To do what you set dynamically, I don't think Linq-to-sql-inheritance will cut it. Or partial methods. Reflection may be your best bet. That is: One main Trigger class with a monster of the IsMet () method, which reads the TriggerType line and then searches for assemblies in the folder, loads one with the corresponding name and finds the corresponding class and calls the IsMet method in the class using reflection, then returns the result ... etc.

edit3: or, use instead of ORG instead of LINQ-to-sql instead of ORM. There is a chance that NHibernate will be able to perform dynamic inheritance. For instance. see question

edit4: in fact, here someone writes about using NHibernate to do pretty much what you are trying to do: "Creating a dynamic state machine with C # and NHibernate"
http://blog.lowendahl.net/?p=164

0
source

Source: https://habr.com/ru/post/1398590/


All Articles