When calling the Parse method in the Razor ViewEngine, compilation errors are thrown as TemplateComplilationException which contains a list of errors. Those errors refer to temporary filenames, but the files are deleted before you can access them.
static void Main(string[] args)
{
var service = TemplateServiceFactory.CreateTemplateService(Language.CSharp, true);
string result = "";
try
{
result = service.Parse("Hello #DateTime.NowXX ");
}
catch (TemplateCompilationException ex)
{
foreach (var error in ex.Errors)
if (!string.IsNullOrEmpty(error.FileName))
Console.WriteLine( File.ReadAllText( error.FileName ));
} // ^^^^ File does not exist!
Console.WriteLine( result );
Console.ReadKey();
}
(a little background)
I'm using the Razor engine "stand-alone" without MVC. When I call the Parse I want to get as much detailed information as possible to display to the user.
The current v2.1 release doesn't provide the ability to spit out the source code. There is a debugging feature in the new v3 codebase that allows the source code to be pushed out. It doesn't do this by default, because I'm trying to make the code as performant as possible (and generating the code twice (once as CodeDom, once as a string) isn't ideal). You'll need to enable the Debug flag on your configuration:
var config = new TemplateServiceConfiguration { Debug = true };
var service = new TemplateService(config);
This will enable the source code to be read when an exception is thrown.
Point of interest, through testing the Roslyn compiler infrastructure with the v3 codebase, it accepts a string source instead of CodeDom, so I'll likely make a future change to use that instead of CodeDom directly - this in turn means we have direct access to the source code without having to worry about enabling any Debug flag which will likely be deprecated.
v3 (currently v3.0.7beta) is available on Nuget (Install-Package RazorEngine). I was aiming to RTW last weekend but never got round to it.
RazorEngine's TemplateCompilationException is a class that wraps a CompilerErrorCollection that contain CompilerError objects, so the most details you could possibly get from the TemplateCompilationException CompilerError objects are their respective properties, which appears to be enough to debug with. Consider and try this example
try
{
Razor.Parse("My erroneous #DateTime.Now.foo()");
}
catch(TemplateCompilationException ex)
{
foreach(var error in ex.Errors)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Compile Error Num: \t" + error.ErrorNumber);
sb.AppendLine("Error Text:\n\t" + error.ErrorText);
Console.WriteLine(sb.ToString());
}
Console.WriteLine("Erroneous Template:\n\t" + ex.Template);
}
When I run my example this is what I get, which tells you the error(s) that was encountered and you can dump the template data to reference for your users.
Compile Error Num: CS1061
Error Text:
'System.DateTime' does not contain a definition for 'foo' and no
extension method 'foo' accepting a first argument of type
'System.DateTime' could be found (are you missing a using directive
or an assembly reference?)
Erroneous Template:
My erroneous #DateTime.Now.foo()
Related
I'm trying to read a json string into memory and get this undocumented error msg
$ mcs -r:FortnoxAPILibrary.dll -r:npgsql.dll -r:System.Data.dll -r:Newtonsoft.Json.dll Vouchers.cs
Vouchers.cs(44,18): error CS0103: The name `JArray' does not exist in the current context
Compilation failed: 1 error(s), 0 warnings
My code is
var json = System.IO.File.ReadAllText("test.json");
var objects = JArray.Parse(json); // parse as array
foreach(JObject root in objects)
{
foreach(KeyValuePair<String, JToken> app in root)
{
var appName = app.Key;
var description = (String)app.Value["Description"];
var value = (String)app.Value["Value"];
Console.WriteLine(appName);
Console.WriteLine(description);
Console.WriteLine(value);
Console.WriteLine("\n");
}
}
Where is it documented how this should work?
You are more than likely missing a using statement.
using Newtonsoft.Json.Linq;
Every piece of C# code you write, except for core types, requires a using statement pointing to any dependencies.
C# libraries often don't document the using statement requirements for a block of code. Maybe an oversight, but most users are using an IDE, which warns of the missing statement and offers options to automatically insert them.
It is not documented that I must include this line.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
I use CSScriptLibrary.dll to execute C# code in my application which runs on both Windows and Linux. Problem is, right now, I need to use #pragma disable warning to disable all kinds of warnings that may arise in order to get the scripts to compile on Mono which is a very ugly hack.
// the following simple script will not execute on Mono due to a warning that a is not used.
var code = "public class Script { public object Run() { var a=1; return 2+3; }}"
// here is how the script is executed using CsScriptLibrary
try
{
var asm = new AsmHelper(CSScript.LoadCode(code, "cs", null, true));
// if we reach that point, the script compiled
var obj = asm.CreateAndAlignToInterface<IScript>("*");
// now run it:
var result=obj.Run();
}
catch (CompilerException e)
{
// on .net compiler exceptions are only raised when there are errors
// on mono I get an exception here, even for warnings like unused variable
}
I already tried to set the default compiler parameters of CSScript to instruct the mono compiler to disregard warnings. Here is what I tried (based on documentation of compiler switches of Mono compiler:
CSScript.GlobalSettings.DefaultArguments = "-warn:0 -warnaserror-";
But I had no success and I am not even sure if this is the right way to go. Anyway, for the sake of completeness I note here that CSScript.GlobalSettings.DefaultArguments defaults to /c /sconfig /co:/warn:0 in CSScript.
Does anyone know how to get CSScript.LoadCode to disregard warnings on Mono or at least not treat them as errors?
Here are two solutions (found with help of Oleg Shilo). You can either include the desired compiler option directly in the script:
//css_co -warn:0
using System;
...
or you can substitute CSScript.LoadCode with LoadWithConfig, which allows passing compiler options directly. Something like this:
static public Assembly LoadCode(string scriptText, bool debugBuild, params string[] refAssemblies)
{
string tempFile = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() +".cs";
try
{
using (StreamWriter sw = new StreamWriter(tempFile))
sw.Write(scriptText);
return LoadWithConfig(scriptFile, null, debugBuild, CSScript.GlobalSettings, "-warn:0", refAssemblies);
}
finally
{
if (!debugBuild)
{
//delete temp file
}
}
}
It should be noted, that the second solution will bypass the built in assembly caching performed in LoadCode. It is easy enough to cache the compiled script object though.
My issue goes like this:
There is a project called myframework. It has some extension methods defined in it as follows:
namespace myframework
{
public static class Helpers
{
public static bool ContainsAll(this string obj, string[])
{
return true;
}
}
}
It also has some other stuff like interfaces, etc, etc.
There is a second class I generate via System.CodeDom classes. The generated output is somewhat like this:
using myframework;
public class A: IMyFrameworkInterface
{
public void foo()
{
string s ="HELLO";
if(s.ContainsAll(some_arr))
return;
}
//More methods defined...
}
The compiler options I pass which is created prior to the actual compile call references the correct assemblies
var cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("myframework.dll");
The code compilation modules are written in a different project. The particular class responsible for this also nicely gives us access to a list of CompilerError object via which we can learn the result of compilation.
Issue1: When I tried this in an asp.net project the compiler threw error saying it could not find metadata file myframework.dll (despite it being referenced in the project).
Issue2: When I tried it with a windows forms project. It gave a different error. This time saying that string does not contain definition for ContainsAll()
How to solve these two specific problems?
Found out the answer to this after a bit digging up. I was using .net framework 3.5. The codedom compiler apis targets v2.0 of the framework by default. Hence, you have to manually specify the correct framework:
var cp = new CompilerParameters(
new Dictionary<string,string>() { {"CompilerVersion", "v3.5"} });
For the compilation to work within an asp.net environment you'd have to actually point the references to the correct location. Hence you'd have to do something like follows:
cp.ReferencedAssemblies.Add(
HttpContext.Current.Server.MapPath(
"bin\\myframework.dll"));
My references:
http://blogs.msdn.com/b/lukeh/archive/2007/07/11/c-3-0-and-codedom.aspx
.Net 3.5 CodeDom Compiler generating odd errors
And comments in the question's post. :)
I am using System.Web.Helpers.Json to deserialize some JSON into dynamic in NET 4. The following line fails with this error: TypeInitializationException: Attempt by method 'System.Web.Helpers.Json..cctor()' to access method 'System.Web.Helpers.Json.CreateSerializer()' failed.
var json = Json.Decode(response);
The response is lengthy but valid JSON. What could be the matter here? I have tried LINQPad with a short handcrafted JSON and it worked. Is this a configuration issue of some sort?
[EDIT]
Here is the actual sample JSON. It appears the content is pretty much irrelevant. When this is run in a brand new Console application or LINQPad, it works as expected. But if you try to run the same code from a brand new Windows Forms application, it barfs with the above error.
var json = Json.Decode("{\"r\":{\"0\":{\"id\":\"2\"},\"1\":{\"id\":\"33\"}}}");
[EDIT2]
Actually, it turns out this has nothing to do with project types. The exception is thrown if the project is being debugged. If it is simply run, the exception does not occur. Strange, eh?
I forgot about this question and I found my answer in the meantime. I think it was somewhere on Microsoft's Connect site but I am not sure. So let's share it now.
Basically, in order to workaround this problem you need to make sure "Enable the Visual Studio hosting process" is unchecked in your project's settings under Debug. I am not sure why it's happening but this is definitely a way to "fix" it. I stopped searching for answers once I found out about this. It was good enough for me.
This can also happen if you are running in a partial trust.
Check the exception description here for possible reasons.
I don't know if this will apply to you, since you are not running in a web context, but this is what that link describes:
This exception is thrown in situations such as the following:
A private, protected, or internal method that would not be accessible from normal compiled code is accessed from partially
trusted code by using reflection.
A security-critical method is accessed from transparent code.
The access level of a method in a class library has changed, and one or more assemblies that reference the library have not been
recompiled.
There is problem in the inbuilt json class.
If you want to achieve this in alternate way, please use the below code:
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new DynamicJavaScriptConverter[] { new DynamicJavaScriptConverter() });
var result = WrapObject(serializer.DeserializeObject(value)); // here you will have result.
private object WrapObject(object value)
{
IDictionary<string, object> values = value as IDictionary<string, object>;
if (values != null)
{
return new DynamicJsonObject(values);
}
object[] arrayValues = value as object[];
if (arrayValues != null)
{
return new DynamicJsonArray(arrayValues);
}
return value;
}
Further to Roland's answer: some assembly mismatches listed can be fixed in the AssemblyInfo.cs file.
The offending line in my AssemblyInfo was this:
[assembly: AllowPartiallyTrustedCallers]
Removing this allowed me to access the public property (on a public class) that I was trying to set from another assembly that had dynamically loaded this assembly.
I am using LoadFrom(), to load dlls, but for some reason this load function doesn't work on all dlls,
i want to load 3000 dlls to get from each one the copyright attribute.
my code :
class ReverseDLL
{
private Assembly assembly;
private AssemblyDescriptionAttribute desc;
private AssemblyTitleAttribute title;
private AssemblyCopyrightAttribute copyRight;
public string getCopyright(string path)
{
try
{
//assembly = System.Reflection.Assembly.Load(System.IO.File.ReadAllBytes(path));
assembly = System.Reflection.Assembly.LoadFrom(path);//"C:\\Windows\\winsxs\\x86_microsoft.vc90.debugcrt_1fc8b3b9a1e18e3b_9.0.30729.1_none_bb1f6aa1308c35eb\\msvcm90d.dll");//path);// LoadFrom(path);
desc = (AssemblyDescriptionAttribute)
AssemblyDescriptionAttribute.GetCustomAttribute(
assembly, typeof(AssemblyDescriptionAttribute));
title = (AssemblyTitleAttribute)
AssemblyTitleAttribute.GetCustomAttribute(
assembly, typeof(AssemblyTitleAttribute));
copyRight = (AssemblyCopyrightAttribute)AssemblyCopyrightAttribute.GetCustomAttribute(assembly, typeof(AssemblyCopyrightAttribute));
}
catch
{
this.copyRight = new AssemblyCopyrightAttribute("");
}
if (this.copyRight == null)
this.copyRight = new AssemblyCopyrightAttribute("");
return copyRight.Copyright;
}
}
I don't know about the reflection problem without you providing more info (such as the error), but you could also try access the file itself:
string copyright = FileVersionInfo.GetVersionInfo(path).LegalCopyright;
This accesses the file-system meta-data (like you would see in explorer), and has the advantage of working for both managed and unmanaged dlls; but it requires that meta-data to exist (it doesn't look at the attribute).
Edit: a quick check indicates that (as expected) the compiler does check for this attribute and populate the file meta-data correctly.
Have you tried stopping on exceptions? Ctrl + Alt + E, stop on framework exceptions when they are thrown. The exception message should give you some information as to why the DLL couldn't be loaded.
Using reflection is not the optimal approach, as some of the dll may have dependencies you don't have.
Using a metadata parser can give you the things you want,
http://ccimetadata.codeplex.com/
http://www.mono-project.com/Cecil
The way Marc mentioned does not work for most .NET specific metadata.