'Console' does not contain a definition for 'WriteLine' exception when using Evaluator.LoadCode<T>

4,763 views
Skip to first unread message

rjcarr...@gmail.com

unread,
Feb 7, 2014, 6:07:16 PM2/7/14
to cs-s...@googlegroups.com
I have the following script file:

using System;
using myNamespace.Interfaces;

public class Console : IOutput
{
    public void Execute(IEvent value)
    {
        Console.WriteLine(value.AsString());
    }

    public void Initialize(params string[] args)
    {
    }
}

and I'm evaluating it in the following way:

string code = File.ReadAllText(fileName);
Evaluator evaluator = CSScript.Evaluator.ReferenceAssembliesFromCode(code, Path.DirectoryName(fileName));
IOutput result = evaluator.LoadCode<IOutput>(code);

When LoadCode executes an exception is thrown with the message: 'Console' does not contain a definition for 'WriteLine'.  I know that there isn't a syntax/spelling issue in the script as I've run it as a normal compiled class fine.  I've checked the assemblies loaded by the Evaluator and everything I'd expect is there.  I also have a much more complicated script that use the same interface but references a bunch of external dlls and that works fine.

Anyone got any ideas what I'm missing?

Oleg Shilo

unread,
Feb 7, 2014, 6:51:47 PM2/7/14
to cs-s...@googlegroups.com
This is because in your code you have two classes 'Console'. Your script class 'Console' and 'System.Console'

If you change your code as follows it will resolve the ambiguity:

System.onsole.WriteLine(value.AsString());

Cheers,
Oleg

--
You received this message because you are subscribed to the Google Groups "CS-Script" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cs-script+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Robert Carrington

unread,
Feb 7, 2014, 7:41:30 PM2/7/14
to cs-s...@googlegroups.com
Gah!  Thank you for the swift response, it's now working perfectly.

I had hoped whatever was the cause of that issue was also the cause of another issue I'm having but I don't think it is.  I have another script file that's quite a lot more involved:

using myNamespace.Events;
using myNamespace.Interfaces;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reactive;
using System.Reactive.Linq;

public class FileInput : IInput
{
    private string _fileName;
    private string _timeStampRegEx;
    private FileSystemWatcher _watcher;
    private long _position;

    public IObservable<IEvent> Execute()
    {
        Initialize();

        return Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(action => { _watcher.Changed += action; }, action => { _watcher.Changed -= action; })
            .Throttle(TimeSpan.FromSeconds(1))
            .SelectMany(SelectLines);
    }

    public void Initialize(params string[] args)
    {
        _fileName = args[0];
        if (args.Length > 1)
        {
            _timeStampRegEx = args[1];
        }
    }

    private void Initialize()
    {
        string directory = Path.GetDirectoryName(_fileName) ?? string.Empty;
        string fileName = Path.GetFileName(_fileName) ?? string.Empty;
        _watcher = new FileSystemWatcher(directory, fileName);
        _watcher.NotifyFilter = NotifyFilters.Size;
        _watcher.EnableRaisingEvents = true;

        _position = GetEndPosition();
    }

    private IEnumerable<IEvent> SelectLines(EventPattern<FileSystemEventArgs> e)
    {
        var currentSize = new FileInfo(e.EventArgs.FullPath).Length;
        if (currentSize < _position)
        {
            _position = 0;
        }
        using (FileStream fs = new FileStream(_fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            using (StreamReader sr = new StreamReader(fs, true))
            {
                fs.Seek(_position, SeekOrigin.Begin);
                string line;
                while ((line = sr.ReadLine()) != null)
                {
                    yield return new TextEvent(line, _timeStampRegEx);
                }
                _position = fs.Position;
            }
        }
    }

    private long GetEndPosition()
    {
        using (FileStream fs = new FileStream(_fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            using (StreamReader sr = new StreamReader(fs, true))
            {
                sr.ReadToEnd();
                return fs.Position;
            }
        }
    }
}

Again I'm using it in the same way as my original example:

string code = File.ReadAllText(fileName);
Evaluator evaluator = CSScript.Evaluator.ReferenceAssembliesFromCode(code, Path.DirectoryName(fileName));
IInput result = evaluator.LoadCode<IInput>(code);

I have another IInput script that works fine that uses all the same usings (+ a few extra) except System.IO and System.Reactive (which is in the same dll as System.Reactive.Linq called System.Reactive.Linq.dll).  I've checked the Evaluator and as far as I can see it has all the assembly references required.

I'm getting the following error:

Additional information: {interactive}(13,13): error CS0246: The type or namespace name `FileSystemWatcher' could not be found. Are you missing a using directive or an assembly reference?

{interactive}(45,45): error CS0246: The type or namespace name `EventPattern' could not be found. Are you missing a using directive or an assembly reference?

{interactive}(20,44): error CS0246: The type or namespace name `FileSystemEventHandler' could not be found. Are you missing a using directive or an assembly reference?

{interactive}(20,68): error CS0246: The type or namespace name `FileSystemEventArgs' could not be found. Are you missing a using directive or an assembly reference?

{interactive}(36,28): error CS0103: The name `Path' does not exist in the current context

{interactive}(36,33): error CS0023: The `.' operator cannot be applied to operand of type `<fake$type>'

{interactive}(37,27): error CS0103: The name `Path' does not exist in the current context

{interactive}(37,32): error CS0023: The `.' operator cannot be applied to operand of type `<fake$type>'

{interactive}(38,24): error CS0246: The type or namespace name `FileSystemWatcher' could not be found. Are you missing a using directive or an assembly reference?

{interactive}(38,9): error CS0103: The name `_watcher' does not exist in the current context

{interactive}(39,33): error CS0103: The name `NotifyFilters' does not exist in the current context

{interactive}(39,47): error CS0023: The `.' operator cannot be applied to operand of type `<fake$type>'

{interactive}(39,9): error CS0103: The name `_watcher' does not exist in the current context

{interactive}(39,18): error CS0023: The `.' operator cannot be applied to operand of type `<fake$type>'

{interactive}(39,18): error CS0131: The left-hand side of an assignment must be a variable, a property or an indexer

{interactive}(40,9): error CS0103: The name `_watcher' does not exist in the current context

{interactive}(40,18): error CS0023: The `.' operator cannot be applied to operand of type `<fake$type>'

{interactive}(40,18): error CS0131: The left-hand side of an assignment must be a variable, a property or an indexer

Oddly all the errors are on types are from the 2 types (System.IO and System.Reactive) that aren't in my other script file.  I wonder if it was due to load order even if I don't load the other script, this script still fails in the same way.

Oleg Shilo

unread,
Feb 7, 2014, 7:59:31 PM2/7/14
to cs-s...@googlegroups.com
Because you are referencing assemblies manually probably it is better to disable any automatic referencing.

Thus before loading the script call CSScript.Evaluator.ReferenceDomainAssemblies(DomainAssemblies.None)
  
This way you will always be in full control of what assemblies are referenced from the script.

Saying that, you can benefit from the opposite approach. Do not reference assembly manually. Instead ensure the host app has all required assemblies loaded (and possibly tested) and make the same call as above but with one of the others DomainAssemblies  values (e.g. AllStaticNonGAC). 

Oleg

--

Robert Carrington

unread,
Feb 8, 2014, 3:08:06 AM2/8/14
to cs-s...@googlegroups.com, osh...@gmail.com
Is this a limitation of the Evaluator approach?  Ideally I'd like to have certain assemblies referenced automatically (common System, System.Reactive and myNamespace in this case) but allow the script writer to reference any other assemblies they may need.  In this case it's as though it's not using any of the referenced assemblies but I can see them all in Evaluator.Assembly2Defintion collection.  For reference this script does works absolutely fine:

//css_reference MongoDB.Bson.dll;
//css_reference MongoDB.Driver.dll;
using myNamespace.Events;
using myNamespace.Interfaces;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using MongoDB.Driver.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;

public class MongoDbInput : IInput
{
    private string _uri;
    private string _database;
    private string _collection;

    private MongoCollection<BsonDocument> _mongoCollection;
    private BsonValue _lastId;

    public IObservable<IEvent> Execute()
    {
        return Observable.Interval(TimeSpan.FromSeconds(1)).SelectMany(_ => GetEvents());
    }

    public void Initialize(params string[] args)
    {
        _uri = args[0];
        _database = args[1];
        _collection = args[2];
    }

    private IEnumerable<IEvent> GetEvents()
    {
        if (_mongoCollection == null)
        {
            _mongoCollection = GetMongoCollection();
            _lastId = null;
        }
        if (_lastId == null)
        {
            _lastId = GetLastId(_mongoCollection);
        }
        var query = Query.GT("_id", _lastId);
        foreach (var document in _mongoCollection.Find(query).SetSortOrder("$natural"))
        {
            yield return new BsonEvent(document);
            _lastId = document["_id"];
        }
    }

    private MongoCollection<BsonDocument> GetMongoCollection()
    {
        var client = new MongoClient(_uri);
        var server = client.GetServer();
        var database = server.GetDatabase(_database);
        return database.GetCollection(_collection);
    }

    private BsonValue GetLastId(MongoCollection<BsonDocument> collection)
    {
        BsonDocument lastRecord = null;
        if (collection.Exists() && collection.Count() > 0)
        {
            lastRecord = collection.AsQueryable().Last();
        }
        return (lastRecord != null) ? lastRecord["_id"] : BsonMinKey.Value;
    }

    private class BsonEvent : IEvent
    {
        private readonly BsonDocument _document;

        public BsonEvent(BsonDocument document)
        {
            _document = document;

            var objectId = _document["_id"] as BsonObjectId;
            TimeStamp = (objectId != null) ? objectId.Value.CreationTime : DateTime.Now;
        }

        public DateTime TimeStamp { get; private set; }

        public string AsString()
        {
            return string.Format("[{0}] {1}", TimeStamp, _document.ToJson());

Oleg Shilo

unread,
Feb 8, 2014, 6:32:21 AM2/8/14
to cs-s...@googlegroups.com
Great. I am glad it works.

As for your preferred referencing scenario you indeed can freely combine both automatic and explicit referencing. I simply always advice to disable auto-referencing when troubleshooting.

You just need to remember that CSScript.Evaluator is a wrapper around Mono.Evaluator and it does not support asm referencing per script execution but per evaluator. 

Thus in your case I would use the following:

CSScript.Evaluator.ReferenceDomainAssemblies(DomainAssemblies.AllStaticNonGAC)
                  .ReferenceAssembliesFromCode(script)
                  .LoadCode(script);

Robert Carrington

unread,
Feb 8, 2014, 7:09:19 AM2/8/14
to cs-s...@googlegroups.com, osh...@gmail.com
I'm using a different evaluator for each script so I'm not sure that's the problem.  Also your code doesn't seem to work as ReferenceDomainAssemblies returns void?  I'm using version 3.7.0.0 obtained via nuget if it's a version problem?

Oleg Shilo

unread,
Feb 8, 2014, 7:25:25 AM2/8/14
to cs-s...@googlegroups.com
>I'm using a different evaluator for each script so I'm not sure that's the problem
I am not sure it is entirely correct. If you are using CSScript.Evaluator then it is the same 'compiler service' every time you load the script.

>Also your code doesn't seem to work as ReferenceDomainAssemblies returns void?
My mistake I was doing it from the memory. The corrected code is as follows: 

CSScript.Evaluator.ReferenceDomainAssemblies(DomainAssemblies.AllStaticNonGAC);
CSScript.Evaluator.ReferenceAssembliesFromCode(code)
                  .LoadCode(code);



On Sat, Feb 8, 2014 at 11:09 PM, Robert Carrington <rjcarr...@gmail.com> wrote:
.   I'm using version 3.7.0.0 obtained via nuget if it's a version problem?

Robert Carrington

unread,
Feb 8, 2014, 7:46:40 AM2/8/14
to cs-s...@googlegroups.com, osh...@gmail.com
Ok I'm now using your code but still no luck.  The scenarios I have are:  If I only load the MongoDb script it works successfully.  If I load the MongoDb script and then the File script, MongoDb loads successfully, but File fails with the long error about various missing types or namespaces I posted earlier.  If I load the File script first then I get the following error:

{interactive}(14,13): error CS0433: The imported type `System.IO.FileSystemWatcher' is defined multiple times

{interactive}(46,58): error CS0433: The imported type `System.IO.FileSystemEventArgs' is defined multiple times

{interactive}(21,44): error CS0433: The imported type `System.IO.FileSystemEventHandler' is defined multiple times

{interactive}(40,33): error CS0433: The imported type `System.IO.NotifyFilters' is defined multiple times

Internal(1,1): warning CS1685: The predefined type `System.Runtime.InteropServices.DefaultParameterValueAttribute' is defined multiple times. Using definition from `System.dll'

An overview of what I'm trying to do is:  I have a folder that contains script files that each contain a class that implements the same interface.  At run time I need to be able to load an instance of 1 or more of these script classes (sometimes more than 1 instance of the same script too).  Sometimes these scripts will reference external dlls that are in the same folder as the scripts.  Does this seem possible?

Thankyou for the time and help you've given so far, it's really appreciated.

Oleg Shilo

unread,
Feb 8, 2014, 8:15:04 AM2/8/14
to cs-s...@googlegroups.com
It means that your evaluator referenced System.dll twice. Once automatically from host AppDomain loaded assemblies and another one from the script code. And most likely they are from the different locations.

Thus in your case we have a true assembly collision. It is likely you will be better off by ignoring AppDomain assemblies and load the all assemblies explicitly (+ assemblies from the code)

CSScript.Evaluator.ReferenceDomainAssemblies(DomainAssemblies.None);
 
CSScript.Evaluator.ReferenceAssembly(typeof(Console).Assembly)
                  .ReferenceAssemblyOf<Form>()
                  ...
                  .ReferenceAssembliesFromCode(code)
                  .LoadCode(code);



Robert Carrington

unread,
Feb 8, 2014, 8:49:09 AM2/8/14
to cs-s...@googlegroups.com, osh...@gmail.com
This has just made it worse as I get the defined multiple times error for all scripts now.  I've checked the assemblies I'm explicitly loading and I'm only getting each 1 once.  If I don't load assemblies explicitly, it seems like I get the same results for the following:
CSScript.Evaluator.ReferenceDomainAssemblies(DomainAssemblies.None);
CSScript.Evaluator.ReferenceAssembliesFromCode(code).LoadCode(code);
and:
CSScript.Evaluator.ReferenceDomainAssemblies(DomainAssemblies.AllStaticNonGAC);
CSScript.Evaluator.ReferenceAssembliesFromCode(code).LoadCode(code);

Is there another option than Evaluator that will do what I want without this reference nightmare? It doesn't seem like what I'm trying to do is particularly unusual.

Robert Carrington

unread,
Feb 8, 2014, 11:49:30 AM2/8/14
to cs-s...@googlegroups.com, osh...@gmail.com
I've now got everything working by using:

T result = CSScript.LoadCode(code).CreateObject(typeName).AlignToInterface<T>();

I noticed 1 thing that might explain some of the strange behaviour around the File.csx not working previously.  The Reactive Extensions namespaces aren't very consistent with the .dll they're contained in.  For example System.Reactive.Core.dll and System.Reactive.Interfaces.dll both contain classes in the System.Reactive namespace and System.Reactive.Interfaces namespace doesn't even exist.  Despite all the dll being available, unless I specifically put //css_reference System.Reactive.Interfaces.dll at the top, it doesn't get picked up.  System.Reactive.Core.dll gets picked up fine despite the file name not matching the namespace though so I'm not sure how that works.  On the other hand MongoDb.csx uses MongoDb.Bson.dll which isn't referenced by the main application but as long I copy it into the same folder as the executable it gets picked up without even having to use //css_reference MongDb.Bson.dll.



On Saturday, 8 February 2014 13:15:04 UTC, Oleg Shilo wrote:

Oleg Shilo

unread,
Feb 8, 2014, 6:09:12 PM2/8/14
to cs-s...@googlegroups.com
Hi Robert,

I also did some digging this morning. I had to finish the conversation yesterday as it was already midnight here in Australia. :o)

While I only had your script but not the host code I reconstructed the host code and was able to run the sample.

What I found (to my disappointment) is that Mono.CSharp cannot compile the script. I came to the same conclusion: referencing the assemblies for Mono evaluator in your scenario is a nightmare. And indeed there is something going on with the assemblies/namespaces that confuses Mono compiler. Mono was raising the errors about duplicated Observable type even if all ref asms were unique. And even when I removed all "Observable" it stuck with wired internal mono error about "<fake$type>"

So I also tried the CodeDom approach and it went as a knife through the butter.
Inline image 1

I have to say that after this ordeal I will need to review Mono.Evaluator being a primary recommended hosting model for CS-Script.

And here are a few points for you about the matter.

CodeDOM hosting model is the original CS-Script hosting model. It is mature, simple, intuitive consistent with the practically C# coding paradigms. It beats Mono.Evaluator in all categories except one - being CompilerAsService. 

CodeDOM has a limitation inherited from the CLR assembly loading implementation. Once assembly loaded it cannot be unloaded. While MS acknowledged this as a problem it has no plans to fix it. They told me "it is to difficult to do". Thus I provided a work around by transparently loading the assemblies into temp AppDomain and unloading the whole domain when not needed. Though as any work around it has its own limitations. But if you are not facing the task if executing unlimited number of constantly changing scripts CodeDOM is a better tool for all counts. It can even offer two major features Evaluator cannot: multi-file script and script debugging.

As for the Mono evenuator, it is not that bad but let's say... "rough on edges". 

Regards,
Oleg Shilo

image.png

Robert Carrington

unread,
Feb 8, 2014, 6:26:09 PM2/8/14
to cs-s...@googlegroups.com, osh...@gmail.com
Glad it wasn't just me!  Thanks again for all the help and information, you've gone above and beyond and it's been much appreciated.

Oleg Shilo

unread,
Feb 9, 2014, 3:17:13 AM2/9/14
to cs-s...@googlegroups.com
No worries, I am glad if I can help.

BTW thank you very much for your donation. Your support is greatly appreciated. 

Oleg Shilo

P.S. If you like CS-Script you can also have look at the complete C# Intellisense solution for Notepad++. It is built on top of CS-Script and ICSharpCode.NRefactory/Mono.Cecil: https://csscriptnpp.codeplex.com/
Reply all
Reply to author
Forward
0 new messages