I am trying to create a code analyzer in Roslyn, and I need to analyze SqlCommand usage in a project. I have written the analyzer and it works fine when I test it out in Visual Studio project, but when I am writing unit test and I am trying to get SymbolInfo from SemanticModel and am always getting null.
What am i missing?
string test = #"public class TestClass
{
public void SomeMethod(int x)
{
var command = new SqlCommand(""Some COmmabnd"",new SqlConnection(""conn string""));
command.ExecuteReader();
}
}";
var tree = CSharpSyntaxTree.ParseText(test);
var systemDataReference = MetadataReference.CreateFromFile(typeof(System.Data.IDbCommand).Assembly.Location);
var systemConfigurationReference = MetadataReference.CreateFromFile(typeof(ConfigurationManager).Assembly.Location);
var systemTransactionReference = MetadataReference.CreateFromFile(typeof(Transaction).Assembly.Location);
var systemXmlnReference = MetadataReference.CreateFromFile(typeof(XPathDocument).Assembly.Location);
var system = MetadataReference.CreateFromFile(typeof(Uri).Assembly.Location);
var mscorRef = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var systemCore = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location);
var systemNumerics = MetadataReference.CreateFromFile(typeof(BigInteger).Assembly.Location);
var compilation = CSharpCompilation.Create("TestCompilatin", new[] {tree},
new[]
{
mscorRef, system, systemXmlnReference, systemTransactionReference, systemDataReference,
systemConfigurationReference,systemCore,systemNumerics
});
var semanticModel = compilation.GetSemanticModel(tree);
var invocationExpressions = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>();
foreach (var invocationExpressionSyntax in invocationExpressions)
{
var memeber = invocationExpressionSyntax.Expression as MemberAccessExpressionSyntax;
var symbolInfo = semanticModel.GetSymbolInfo(memeber);
}
semanticModel.GetSymbolInfo() returns SymbolInfo with null symbol for anything that I try.
Another possible cause is that the source file for the invoked method has not been syntactically parsed and added to the compilation. You can always get some information about the method being invoked in the caller's syntax tree, but unless the invoked method has already been added to the compilation, it won't be available in the semantic model. This is painfully obvious after thinking about it for a bit. Of course the semantic model will not have information about a method that has not been processed. It is possible to add multiple syntax trees to a single compilation using AddSyntaxTrees() method. Note that this method returns a new compilation instance.
Hopefully this will save someone a little bit of time and I haven't embarrassed myself too much by posting this answer.
Related
This question already has an answer here:
Entity Core Dynamic LINQ LIKE function not found [closed]
(1 answer)
Closed 4 months ago.
Note: If you have the same problem and found this question before the "duplicated" one, be aware that the answer to that question does not work. I marked the only working solution below.
I'm trying to follow this guide which states that this should be possible in Dynamic LINQ:
var config = new ParsingConfig { ResolveTypesBySimpleName = true };
var example2 = Cars.Where(config, "DynamicFunctions.Like(Brand, \"%t%\")");
example2.Dump();
I'm using string concatination to build a complex query:
IQueryable<DtcViewEntity> queryable = ...
string[] values = { "%a%" };
string myOperator = "and"; // or sometimes "or"
bool isNot = false; // or sometimes "true"
var query = string.Join($" {myOperator} ", values.Select((value, index) => $"DynamicFunctions.Like(MyColumn, #{index})"));
if (isNot) query = $"not ({query})";
var config = new ParsingConfig { ResolveTypesBySimpleName = true };
return queryable.Where(config, query, values);
But the actual error can be reproduced with code that looks almost identical to the example in the guide:
IQueryable<DtcViewEntity> queryable = ...
var config = new ParsingConfig { ResolveTypesBySimpleName = true };
var result = queryable.Where(config, "DynamicFunctions.Like(MyColumn, \"%a%\")").ToList();
And I get:
System.Linq.Dynamic.Core.Exceptions.ParseException : Enum type 'DynamicFunctions' not found
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAsEnum(String id)
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseMemberAccess(Type type, Expression expression)
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseIdentifier()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParsePrimaryStart()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParsePrimary()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseUnary()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseMultiplicative()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAdditive()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseShiftOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseComparisonOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseLogicalAndOrOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseIn()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAndOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseOrOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseLambdaOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseNullCoalescingOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseConditionalOperator()
I've seen this code as well, but using var config = ParsingConfig.DefaultEFCore21; will result in
System.Linq.Dynamic.Core.Exceptions.ParseException : Enum type 'DynamicFunctions' not found
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAsEnum(String id)
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseMemberAccess(Type type, Expression expression)
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseIdentifier()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParsePrimaryStart()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParsePrimary()
After adding Microsoft.EntityFrameworkCore.DynamicLinq to the dependencies I get:
System.Linq.Dynamic.Core.Exceptions.ParseException : No applicable method 'Like' exists in type 'DynamicFunctions'
Falco Alexander even found a "working example" form ZZZ Projects that produces the very same error message:
Unhandled exception. Enum type 'DynamicFunctions' not found (at index 21)
Command terminated by signal 6
I can't find any information on this exception, so my question is: what am I doing wrong?
my code runs fine if I use EF.Functions directly
var result = context.Cars.Where(c => EF.Functions.Like(c.Brand, "%a%")).ToList();
also testing with Linqpad and Northwind localDB with a different non-EF provider.
var config = new ParsingConfig { ResolveTypesBySimpleName = true };
var example2 = from c in Customers
where SqlMethods.Like(c.City, "[aeiou]%")
select c;
Get Extension methods from this answer. It contains GetItemsPredicate function, which will help in building predicate.
Then we can generate needed predicate without Dynamic.Linq and avoid raw text parsing.
// input parameters
string[] values = { "%a%" };
var isOr = true;
var isNot = false;
var predicate = queryable.GetItemsPredicate(values, (e, v) => EF.Functions.Like(e.MyColumn, v), isOr);
if (isNot)
{
// inversion
predicate = Expression.Lambda<Func<DtcViewEntity, bool>>(Expression.Not(predicate.Body), predicate.Parameters);
}
// applying generated predicate
queryable = queryable.Where(predicate);
Looks like the errors you are getting have to do with Linq not being
able to "transform" the data so it can parse it or something along
those lines Is it possible for you instead of the "Like" operator you
use the "Contains"?
var Something = SomethingElse.Filter(x => x.Argument.Contains(Description));
I have the following code
var dynamicAdd2 = new DynamicMethod("add",
typeof(string),
new[] { typeof(TestType) },
typeof(Program).Module, false);
var add2Body = typeof(Program).GetMethod("add2").GetMethodBody().GetILAsByteArray();
var dynamicIlInfo = dynamicAdd2.GetDynamicILInfo();
var ilGenerator = dynamicAdd2.GetILGenerator();
dynamicIlInfo.SetLocalSignature(SignatureHelper.GetLocalVarSigHelper().GetSignature());
dynamicIlInfo.SetCode(add2Body, 1000);
var test2 = (Func<TestType, string>)dynamicAdd2.CreateDelegate(typeof(Func<TestType, string>));
var ret2 = test2(new TestType()); // <-- Exception
the add2:
public string add2(TestType digit)
{
return digit.Name;
}
the testType:
public class TestType
{
public string Name = "test";
}
I get a InvalidProgrammException, no more information
So I expect that the creation of the dynamic method fails. I think the dynamic Method can not find the references to the TestClass. Or what can be wrong in this case? Or what can I do to get a hint where the problem lies? the Exception brings not the needed infos...
You cannot directly copy IL stream from existing method to dynamic method, because IL uses so called tokens (32-bit numbers) to represent types, methods or fields. For the same field, value of token can be different in different modules, so byte-copying method IL stream without replacing tokens results in invalid program.
Second problem is that because add2 is instance method (not static), you must add instance of type that this method belongs to as first argument of method. In C# this first argument of instance methods is hidden, but IL requires it. Or you can declare method as static to avoid this.
Third problem is that add2 method contains (compiler generated) local variable. You have to add this variable to local signature (using SetLocalSignature() method), otherwise your method would use undeclared variable. (See code bellow to see how to do that).
Solution 1:
First solution is to use GetILGenerator() instead of GetDynamicILInfo(), and rewrite IL stream from scratch. You can use IL disassembler (e.g. ILDASM, .NET Reflector) to get list of instructions for any existing method. Writing these instructions to IlGenerator using IlGenerator.Emit(...) should not be difficult.
static void Main(string[] args)
{
var dynamicAdd2 = new DynamicMethod("add",
typeof(string),
new[] { typeof(Program), typeof(TestType) },
typeof(Program).Module,
false);
var ilGenerator = dynamicAdd2.GetILGenerator();
ilGenerator.DeclareLocal(typeof(string));
ilGenerator.Emit(OpCodes.Ldarg_1);
var fld = typeof(TestType).GetField("Name");
ilGenerator.Emit(OpCodes.Ldfld, fld);
ilGenerator.Emit(OpCodes.Ret);
var test2 = (Func<TestType, string>)dynamicAdd2.CreateDelegate(typeof(Func<TestType, string>), new Program());
var ret2 = test2(new TestType());
}
Solution 2:
If you cannot use IlGenerator and you require direct IL stream manipulation using GetDynamicILInfo, you have to replace tokens in IL stream with values that are valid for generated dynamic method. Replacing tokens generally requires you to know offsets of these tokens in IL stream. Problem is that exact offset depends on compiler (and is even different for Release/Debug build). So you either have to use some IL dissassembler to get these offsets, or write IL parser able to do that (which is not trivial, maybe you can find some library for that). So following code uses kind of "dirty hack" to make it work in this particular case, but does not work generally.
public static void Main()
{
var dynamicAdd2 = new DynamicMethod("add",
typeof(string),
new[] { typeof(Program), typeof(TestType) },
typeof(Program).Module,
false);
var add2Body = typeof(Program).GetMethod("add2").GetMethodBody();
var add2ILStream = add2Body.GetILAsByteArray();
var dynamicIlInfo = dynamicAdd2.GetDynamicILInfo();
var token = dynamicIlInfo.GetTokenFor(typeof(TestType).GetField("Name").FieldHandle);
var tokenBytes = BitConverter.GetBytes(token);
//This tries to find index of token used by ldfld by searching for it's opcode (0x7B) in IL stream.
//Token follows this instructions so I add +1. This works well for this simple method, but
//will not work in general case, because IL stream could contain 0x7B on other unrelated places.
var tokenIndex = add2ILStream.ToList().IndexOf(0x7b) + 1;
Array.Copy(tokenBytes, 0, add2ILStream, tokenIndex, 4);//
//Copy signature of local variables from original add2 method
var localSignature = SignatureHelper.GetLocalVarSigHelper();
var localVarTypes = add2Body.LocalVariables.Select(_ => _.LocalType).ToArray();
localSignature.AddArguments(localVarTypes, null, null);
dynamicIlInfo.SetLocalSignature(localSignature.GetSignature());
dynamicIlInfo.SetCode(add2ILStream, 1);
var test2 = (Func<TestType, string>)dynamicAdd2.CreateDelegate(typeof(Func<TestType, string>));
var ret2 = test2(new TestType());
}
How can accomplish the following in C#?
var someType = new CustomerMessageHandler<CustomerMessage>();
var nType = someType.GetGenericArguments().First().GetType();
// except the next line of code would be a (Action<nType>)
var a = new Action<string>();
Does Reflection.Emit, Expressions, or F# have a good solution for this?
Note: A 'convention-based' approach is not an option for me. And I'm asking to see if this is even possible to push my C#/F# skills.
Sure, MakeGenericType will be able to do this for you.
Type genericAction = typeof(Action<>);
var someType = new CustomerMessageHandler<CustomerMessage>();
var nType = someType.GetGenericArguments().First().GetType();
var actionType = genericAction.MakeGenericType(new[] { nType });
Note however that an Action<T> is a delegate that should actually DO something... So you'd need to write a body for that:
var actualMethod = Delegate.CreateDelegate(actionType, **your method info here**);
I want to have a class in a WPF app that can extract strings from a resx generated class without having to know the actual resx. other than by the params needed to instantitiate the ResourceManager and use GetString()
The test for the project structure shown fails however:
[Test]
public void CanGetString() {
var expected = MainWindow.MenuItem_Header_English; // value is "English"
var baseName = MainWindow.ResourceManager.BaseName;
var asm = typeof (MainWindow).Assembly;
var rm = new ResourceManager(baseName, asm);
var actual = rm.GetString("MenuItem_Header_English"); // returns null
Assert.That(expected, Is.EqualTo(actual));
}
Can someone confirm this should be possible and tell me what I am doing wrong? Is there a better way to read string values embedded resources?
Cheers,
Berryl
will this work for you
var rm = MainWindow.ResourceManager.GetString("MenuItem_Header_English")
var expected = MainWindow.MenuItem_Header_English;
// not sure how you are comparing String value to what looks like a type..
There are many algorithms to evaluate expressions, for example:
By Recursive Descent
Shunting-yard algorithm
Reverse Polish notation
Is there any way to evaluate any mathematical expression using C# .net reflection or other modern .net technology?
Further to Thomas's answer, it's actually possible to access the (deprecated) JScript libraries directly from C#, which means you can use the equivalent of JScript's eval function.
using Microsoft.JScript; // needs a reference to Microsoft.JScript.dll
using Microsoft.JScript.Vsa; // needs a reference to Microsoft.Vsa.dll
// ...
string expr = "7 + (5 * 4)";
Console.WriteLine(JScriptEval(expr)); // displays 27
// ...
public static double JScriptEval(string expr)
{
// error checking etc removed for brevity
return double.Parse(Eval.JScriptEvaluate(expr, _engine).ToString());
}
private static readonly VsaEngine _engine = VsaEngine.CreateEngine();
It's certainly possible. The CodeSnippetCompileUnit class does basically this.
I wrote you some example usage code. You'll need to include these namespaces:
System.CodeDom.Compiler;
System.CodeDom;
Microsoft.CSharp;
System.Reflection;
Here's the code:
string source = #"
class MyType
{
public static int Evaluate(<!parameters!>)
{
return <!expression!>;
}
}
";
string parameters = "int a, int b, int c";
string expression = "a + b * c";
string finalSource = source.Replace("<!parameters!>", parameters).Replace("<!expression!>", expression);
CodeSnippetCompileUnit compileUnit = new CodeSnippetCompileUnit(finalSource);
CodeDomProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
CompilerResults results = provider.CompileAssemblyFromDom(parameters, compileUnit);
Type type = results.CompiledAssembly.GetType("MyType");
MethodInfo method = type.GetMethod("Evaluate");
// The first parameter is the instance to invoke the method on. Because our Evaluate method is static, we pass null.
int result = (int)method.Invoke(null, new object[] { 4, -3, 2 });
Replace 'parameters' and 'expression' by whatever, and you've got yourself a general expression evaluator.
If you get a FileNotFoundException in results.CompiledAssembly, then the snippet failed to compile.
You might also want to take a look at the System.CodeDom.CodeSnippetExpression class. It's used for more specifically reading expressions, but an expression by itself can't be compiled, so you would need to use more CodeDom to build a working class and method around it. This is useful if you want to be able to programmatically manipulate what kind of class you're generating. CodeSnippetCompileUnit is nice to generate an entire working class at once (and simpler for an example) but to manipulate it you would have to do inconvenient string manipulations.
Although using compiler services is a simple and efficient solution, it raises serious security issues if the expression is entered by a user, because it could execute virtually anything.
There's another very simple solution that is much more secure : take advantage of the JScript Eval function. You just need to follow these steps :
Create a js file named JsMath.js :
class JsMath
{
static function Eval(expression : String) : double
{
return eval(expression);
};
}
Compile it into a class library :
jsc /t:library JsMath.js
Reference the JsMath library in your C# project, and use it like that :
double result = JsMath.Eval(expression);
For me Vici.Parser works extremely well: check it out here , it's the most flexible expression parser I've found so far.
(we've used it to set up 'human-readable' business rules, with data provided by an SQL server database)
Examples are available and there's a very good support by the developer (check the website's forum).
ncalc is the best. you can find it in codeplex also in nugget.
NCalc is a mathematical expressions evaluator in .NET. NCalc can parse any expression and evaluate the result, including static or dynamic parameters and custom functions.
I think this is the best way of all. Petar Repac's answer is amazing.
Using the 'expression' argument of the DataColumn object solves incredibly and easily the topic:
static double Evaluate(string expression)
{
var loDataTable = new DataTable();
var loDataColumn = new DataColumn("Eval", typeof(double), expression);
loDataTable.Columns.Add(loDataColumn);
loDataTable.Rows.Add(0);
return (double)(loDataTable.Rows[0]["Eval"]);
}
You can use Math-Expression-Evaluator library which implements Shunting Yard algorithm that I am author of. It supports simple expressions such as 2.5+5.9, 17.89-2.47+7.16, 5/2/2+1.5*3+4.58, expressions with parentheses (((9-6/2)*2-4)/2-6-1)/(2+24/(2+4)) and expressions with variables:
var a = 6;
var b = 4.32m;
var c = 24.15m;
var engine = new ExpressionEvaluator();
engine.Evaluate("(((9-a/2)*2-b)/2-a-1)/(2+c/(2+4))", new { a, b, c});
You can also pass parameters as named variables:
dynamic dynamicEngine = new ExpressionEvaluator();
var a = 6;
var b = 4.5m;
var c = 2.6m;
dynamicEngine.Evaluate("(c+b)*a", a: 6, b: 4.5, c: 2.6);
It supports .Net Standard 2.0 so can be used from .Net Core as well as .Net Full Framework projects and it doesn't have any external dependencies.
To dynamically compile code using the new Roslyn API's, and load the assembly in a .net core project;
string finalSource = ...;
IEnumerable<Assembly> references = ...;
var compilation = CSharpCompilation.Create("Dynamic",
new[] {
SyntaxFactory.ParseSyntaxTree(
finalSource,
CSharpParseOptions.Default
.WithLanguageVersion(LanguageVersion.Latest)
) },
references.Select(a => MetadataReference.CreateFromFile(a.Location)),
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
.WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default)
);
using var ms = new MemoryStream();
var e = compilation.Emit(ms);
if (!e.Success)
throw new Exception("Compilation failed");
ms.Seek(0, SeekOrigin.Begin);
var context = new AssemblyLoadContext(null, true);
var assembly = context.LoadFromStream(ms);
Note that along with any other types required by the source you are compiling. In order to load the compiled assembly within the same process, references will need to include;
AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name == "netstandard").Single(),
typeof(object).Assembly