Best approach for executing scripts pulled from database

75 views
Skip to first unread message

domehead100

unread,
Apr 12, 2012, 1:10:44 PM4/12/12
to CS-Script
I've been looking at CS-Script for the last couple of days. If I have
a database that represents "Task" objects, and each of those Tasks has
a _script member that contains the CS-Script code for a task to be
run, I'm not sure of the recommended approach to executing those
scripts.

My current idea is:
1) User creates a new Task and types script code for the Task into an
editor.
2) When the Task is being saved, the script is first compiled, and the
assembly name is stored in a _scriptAssembly member variable for the
Task.
3) If compilation succeeds, the Task is persisted to the database (but
not the assembly).
4) Later, an instance of the app is started up (perhaps on a different
machine, but pointed at the same database). During startup, the app
iterates all of the Tasks, gets their assembly names, and checks that
each of those assemblies exists in the a folder designated for script
assemblies. I suppose I will also have to store a hash of the
assembly along with the Task in order to know if any assembly listed
in the database is different from what is found locally.
5) Any assemblies that need compiling are then compiled, using the
assembly name from the database.
6) Later, executing an automated Task involves executing the compiled
code.

I'm a bit confused about that last step, and I'm certainly interested
in comments about a different or better way to do this.

For the last step, when the Task is to be executed, the Task object
itself should have its correct assembly name. Is it just a matter
then of using AsmHelper to load the assembly and then calling Invoke?
Is there a better way (perhaps using some kind of interface). I
already have an IScript interface (just an Execute() method). I
shouldn't need to pass any objects from the host to the script, since
library code, which is accessible by the script will have static
methods for retrieving anything the script should need to execute.

Oleg Shilo

unread,
Apr 13, 2012, 12:40:59 AM4/13/12
to cs-s...@googlegroups.com
Hi,

First of all, about  the overall architecture. Yes it is doable. However you have to make some important decisions on the way.

These are the things to consider:

- You may want to store compiled scripts (assemblies) on the database instead of the script text only. This may allow you a simpler client deployment model as the assembly is always the "latest" one and there is no need to compile the script. 

- If you want to compile the script on the client side then you may want to deploy the scripts files to the designated directory and allow the script engine to do the caching. Thus if the script is not changed since the last execution it will not be compiled again but the previous compilation result (assembly) will be used. Thus the whole exercise become a trivial "file system synchronization" solution with the file repository implemented as DB.

- Using interfaces instead of Reflection based invoking is always preferred. Thus using AsmHelper is optional. 

Cheers,
Oleg 

domehead100

unread,
Apr 13, 2012, 1:58:30 AM4/13/12
to CS-Script
Well, I'll have to top post because Google doesn't want to scroll all
the way down to the bottom. :o)

Cheers Oleg for your timely replies.

I don't want to store the scripts in the database at this point, but
I'll keep that in mind. There may be a lot of scripts :o).

But, I think I've worked this out, and it's not too complex. Here's
what I've done:

1) My Task class now stores
a) Script text
b) Assembly name, named after Task's TaskId (e.g., "Scripts\Task_<some
guid>.dll")
c) Sha1 hash of compiled assembly.
d) There is also a public property to get a Sha1 hash of the script
text, though this is not stored with the object.

2) When someone goes to edit a script in the GUI, they are presented
with the script text and three buttons:
a) Compile script (calls CSScript.CompileCode if needed, passed an
assembly name as above, checks hashes on script text from GUI vs
stored text and also checks hash on assembly if it exists; only
compiles if something doesn't match up, stores new assembly hash in in-
memory Task object on successful compile)
b) Execute script (compiles script if necessary, then executes using
"new AsmHelper(Assembly.LoadFile(asmFileName))"
c) Execute and Unload script (compiles if necessary, then executes via
"using (AsmHelper helper = new AsmHelper(asmFileName,
TaskId.ToString(), true))" )

For a project that I'm trying to organize and to some extent automate,
there may be upwards of 1K different scripts. I don't want to be
creating new assemblies every time a script is recompiled, and I don't
want to recompile scripts unnecessarily; rather I want to reuse a
specified filename for the script's assembly based on the TaskId and
only recompile if something changed.

This seems to be working well. I've only come across one real problem
at this point: If a user does "Execute script" (in the same app
domain), then they can't recompile the script until they close and
reopen the GUI app because the assembly filename is in effect hard
coded. Doing "Execute and Unload script" solves that of course. I'll
probably take the plain "Execute script" button out of the GUI.

One goal is to allow collaborative editing of the project that these
scripts will be used for; so I will need to revisit adding some change
counters or locking flags in the database to make sure no one steps on
anyone else.
Reply all
Reply to author
Forward
0 new messages