How execute/run code from string variable at runtime - c#

I am trying to execute code that's in a string variable to get an item from a dictionary
I have tried using CSharpCodeProvider like this:
var text = "IconDescription";
text = "\"" + text + "\"";
var field = "Outcome[" + text + "].Value";
field = "\"" + field + "\"";
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
ICodeCompiler icc = codeProvider.CreateCompiler()
parameters.GenerateExecutable = true;
CompilerResults results = icc.CompileAssemblyFromSource(parameters, field)
var dataTest = JsonConvert.DeserializeObject<DictionaryOutcome.Rootobject>(jsonText);
var t = new List<Outcome>();
var defaultOutcome = dataTest.Response.Outcome.KeyValueOfstringOutcomepQnxSKQu.Select(item => new Outcome
{
DataType = item.Value.DataType,
Value = item.Value.Value1,
Field = item.Key
}).ToList();
defaultOutcome.ToList().ForEach(item =>
{
t.Add(item);
});
the field variable's value is Outcome["IconDescription"].Value, and I want to execute this code to get the value from the Outcome dictionary, using the "IconDescription" Key, and get the value.
Is this possible?
I have tried the following:
var scriptOptions = ScriptOptions.Default
.WithReferences(typeof(Dictionary<string, Outcome>).FullName)
.WithImports("Outcome");
var scripts = CSharpScript.Create<object>(field, scriptOptions);
var resultsx = scripts.RunAsync(null, CancellationToken.None).Result;
And I am getting this error:
error CS0246: The type or namespace name 'Outcome' could not be found (are you missing a using directive or an assembly reference?)

I am struggling to even guess what you are trying to do, but as a starter, consider what you are actually doing by trying to compile that string you are constructing.
Outcome["SomeValue"].Value is not even close to being valid C# code:
it has no scope
it has no entry point
it imports no namespaces
it isn't terminated with a ;
the symbol Outcome is not defined
You're compiling this into an executable, so I don't see how it could have any knowledge of the list of results brought back from deserializing the JSON content, where you haven't specified where you're getting this from.
You haven't specified any evidence that explains why you need such an elaborate solution to merely extract some values from JSON, so a straightforward solution might be to use the built-in features of Newtonsoft.Json:
dataTest[0] selects the first element in the array, when the json object is an array;
dataTest[0]["Outcome"] selects the property Outcome of the first object, which may itself be an object
dataTest[0]["Outcome"]["Value"] selects the property Value of Outcome
All of the string indexes here can be known only at runtime and held in variables. I don't see why you need to do any scripting at all to do this.

Related

How to insert the column to predict (label)

I am building a multiclass classification program and i want to dynamicaly insert train data from a CSV.
I have tried:
var loader = context.Data.CreateTextLoader(
new[]
{
new TextLoader.Column("sentiment", DataKind.String,0),
new TextLoader.Column("content", DataKind.String, 1),
},
// First line of the file is a header, not a data row.
hasHeader: true);
var trainData = loader.Load(_filePath);
var experiment = context.Auto().CreateMulticlassClassificationExperiment(240);
//find best model
var result = experiment.Execute(trainData);
Console.WriteLine(Environment.NewLine);
Console.WriteLine("Best run:");
Console.WriteLine($"Trainer name - {result.BestRun.TrainerName}");
When I run the programm I get this error
System.ArgumentException: 'Provided label column 'Label' not found in training data.'
I know there is a way to create a class on runtime and pass it as a schema in LoadFromText but I haven't been able to make it work yet.
I think I see what you need. In the Execute method, there's an overload that it can take in a ColumnInformation.
Just create an instance of that and a property on it allows you to specify the label column name.
var labelColumnInfo = new ColumnInformation()
{
LabelColumnName = "sentiment"
};
Then, you can pass that into the Execute method.
var result = experiment.Execute(trainData, labelColumnInfo);

ML.NET: Schema mismatch for feature column 'Features'

I'm trying to learn ML.NET/Get into Machine Learning, but I'm stuck at an issue.
My goal is to create a Trained Model that can be used to predict a city based on input.
This code:
var dataPath = "cities.csv";
var mlContext = new MLContext();
var loader = mlContext.Data.CreateTextLoader<CityData>(hasHeader: false, separatorChar: ',');
var data = loader.Load(dataPath);
string featuresColumnName = "Features";
var pipeline = mlContext.Transforms.Concatenate(featuresColumnName, "PostalCode", "CityName")
.Append(mlContext.Clustering.Trainers.KMeans(featuresColumnName, clustersCount: 3));
var model = pipeline.Fit(data);
Which should take an CSV as input (Which contains a list of Cities (Column 0 = Postal Code, Column 1 = CityName), and then add these features to the pipeline, gives the following error:
Unhandled Exception: System.ArgumentOutOfRangeException: Schema mismatch for feature column 'Features': expected Vector<R4>, got Vector<Text>
On the "Fit"- function.
I've done a bit of digging on the GitHub Repo, but I can't seem to find a solution. I'm working from the Iris- example (https://learn.microsoft.com/en-us/dotnet/machine-learning/tutorials/iris-clustering) (Of course with my modifications)
Any ideas?
Using FeaturizeText to transform strings features into a float array ones
var pipeline = mlContext.Transforms
.Text.FeaturizeText("PostalCodeF", "PostalCode")
.Append(mlContext.Transforms.Text.FeaturizeText("CityNameF", "CityName"))
.Append(mlContext.Transforms.Concatenate(featuresColumnName, "PostalCodeF", "CityNameF"))
.Append(mlContext.Clustering.Trainers.KMeans(featuresColumnName, clustersCount: 3));
var model = pipeline.Fit(data);

Fill/update the enum values at runtime in C#

I have windows app where-in i need to fill enum values at runtime by reading a text file named "Controls.txt".
As restriction, i'm not suppose to use dictionary. Below is the default values available in the enum MyControls. I have to use enums only.
public enum MyControls
{
Button1 = 0,
Button2 = 1,
Button3 = 2,
}
If Controls.txt file is available, then content of enum should change like
public enum MyControls
{
btn1 = 0,
btn2 = 1,
btn3 = 2,
}
how do i achieve this. I also came across the link Creating / Modifying Enums at Runtime but could not get idea.
I strongly think you are trying to solve the wrong problem. The value of enum is type-safety. I do not think that filling it up dynamically is a good idea. What would really be useful is to have an enum populated by a text file (for example) even before compilation. You can do this using text templates in VS.
You can find an example in my blog post here: http://skleanthous.azurewebsites.net/post/2014/05/21/Creating-enums-from-the-database-and-using-them-in-Entity-framework-5-and-later-in-model-first
Although my example loads from a db, changing it to load from a text file should be trivial.
Apart from the fact that i agree with the other answer that says that you lose type and compile time safety, using EnumBuilderClass should be the only way (thanks to huMpty duMpty's comment).
// sample "file":
string fileContent = #"
btn1 = 0,
btn2 = 1,
btn3 = 2,
";
var enumBody = fileContent.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => new { bothToken = line.Trim().Trim(',').Split('=') })
.Where(x => x.bothToken.Length == 2)
.Select(x => new { Name = x.bothToken[0].Trim(), Value = int.Parse(x.bothToken[1].Trim()) });
AppDomain currentDomain = AppDomain.CurrentDomain;
AssemblyName asmName = new AssemblyName("EnumAssembly");
AssemblyBuilder asmBuilder = currentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = asmBuilder.DefineDynamicModule(asmName.Name, asmName.Name + ".dll");
string enumTypeName = string.Format("{0}.{1}", typeof(MyControls).Namespace, typeof(MyControls).Name);
EnumBuilder eb = mb.DefineEnum(enumTypeName, TypeAttributes.Public, typeof(int));
foreach(var element in enumBody)
{
FieldBuilder fb1 = eb.DefineLiteral(element.Name, element.Value);
}
Type eType = eb.CreateType();
foreach (object obj in Enum.GetValues(eType))
{
Console.WriteLine("{0}.{1} = {2}", eType, obj, ((int)obj));
}
Output:
Namespacename.MyControls.btn1 = 0
Namespacename.MyControls.btn2 = 1
Namespacename.MyControls.btn3 = 2
Well, I agree that the use case above is not something I would use. I, however, do not agree when it comes to there being no use for it. We use for example use enums to classify string values for machine learning modules. We write code at runtime to use it at runtime and grouping enums is a hell of a lot faster than grouping and analysing strings. There is nothing good when using strings in large qualities. They are problematic when doing a comparison, memory allocation, garbage collections, grouping, sorting, there are just too many bytes.
Databases that manage large volumes of data will generate a hash of a string and store that, then compare the strings hash (not unique but a number) and the string at the same statement making the TSQL language use the more definitive index on the hash field to narrow the search, then comparing the string values to make sure the right value is used. in TSQL one would do it this way:
SELECT *
FROM Production.Product
WHERE CHECKSUM(N'Bearing Ball') = cs_Pname
AND Name = N'Bearing Ball';
GO
but in .net we keep thinking that comparing strings is the way to go.
It makes little sense for me to dump my code here as it is proprietary but that there is plenty of good samples out there, an Article by Bob Dain shows line by line how this can be done and is located here
A snippet of his solution looks like this:
using System;
using System.Reflection;
using System.IO;
namespace RemoteUser
{
public class RemoteUserClass
{
public RemoteUserClass()
{
// Load the remote assembly
AssemblyName name = new AssemblyName();
name.CodeBase = "file://" + Directory.GetCurrentDirectory() +
"ThirdPartyDll.dll";
Assembly assembly = AppDomain.CurrentDomain.Load(name);
// Instantiate the class
object remoteObject =
assembly.CreateInstance("ThirdPartyDll.ThirdPartyClass");
Type remoteType =
assembly.GetType("ThirdPartyDll.ThirdPartyClass");
// Load the enum type
PropertyInfo flagsInfo =
remoteType.GetProperty("ThirdPartyBitFields");
Type enumType = assembly.GetType("ThirdPartyDll.BitFields");
// Load the enum values
FieldInfo enumItem1 = enumType.GetField("AnotherSetting");
FieldInfo enumItem2 = enumType.GetField("SomethingElse");
// Calculate the new value
int enumValue1 = (int)enumItem1.GetValue(enumType);
int enumValue2 = (int)enumItem2.GetValue(enumType);
int currentValue = (int)flagsInfo.GetValue(remoteObject, null);
int newValue = currentValue | enumValue1 | enumValue2;
// Store the new value back in Options.FieldFlags
object newEnumValue = Enum.ToObject(enumType, newValue);
flagsInfo.SetValue(remoteObject, newEnumValue, null);
// Call the method
MethodInfo method = remoteType.GetMethod("DoSomeGood");
method.Invoke(remoteObject, null);
}
}
}
One can use the System.Reflection.Emit namespace for many things, one can generate a class that makes a license key for one. One can also write code, and code writing and updating code is the future.

Change SSIS variable with reference in C#?

I access a read write a "SSIS variable" with C# in script task (Inside SSIS, an ETL tool. Don't worry about the SSIS part. Lets look at the C# only). Its Dts.Variables["strRope"].Value = "onFire"; Is there any way I could refer
to a SSIS variable without using this big name ? I was thinking of -
Object var = Dts.Variables["strRope"].REFERENCE_TO_VARIABLE;
Now, if I want assign a new value to Dts.Variables["strRope"], can I simply say var = (String) "Ten thousand thundering typhoons"; .
Is there any way I can do such a thing ?
EDIT Code example -
public void Main()
{
object var = Dts.Variables["strRope"].Value;
MessageBox.Show("original value = " + Dts.Variables["strRope"].Value.ToString());//original value = "Hello World"
//Try to change the value of Dts.Variables["strRope"].Value using var ???
var = (object)"Hello cruel world !";
MessageBox.Show("new value = " + Dts.Variables["strRope"].Value.ToString());//new value = ???
Dts.TaskResult = (int)ScriptResults.Success;
}
Note that var is a C# 3.0 (and above) keyword, and you can use it instead of object to save on typing and to avoid explicitly casting the type. Try using it along the following lines,
var strRope = Dts.Variables["strRope"];
if (strRope.Value == "onFire") { ... }
strRope.Value = "Ten thousand thundering typhoons";

Passing .FindOne() result to variable for gridView - C# .net

I am using a directory search in a C# .NEt web page code behind. I do not know how to have the result added to the text of a cell. The results that are shown in column is the text "System.DirectoryServices.SearchResult". I supect that I am not passing the attribute correctly when I add the result to string.
String Name = userName;
DirectorySearcher ds = new DirectorySearcher();
ds.Filter = "samaccountname=" + Name;
ds.PropertiesToLoad.Add("userAccountControl");
SearchResult sr = ds.FindOne();
e.Row.Cells[5].Text = sr.ToString();
As you can see, I am passing sr.ToString() to the cell. This seems to be incorrect but I do not know how else to pass the result to a variable that I can then convert to string. I evne tried this:
var result1 = sr;
string result2 = result1.ToString();
e.Row.Cells[5].Text = result2;
From the msdn site on SearchResult. It looks like you want to set it to sr.Path? There is no overload for ToString() so it's just going to return the default for the object.
In the SearchResult - try something like sr["userAccountControl"].ToString(). The search result object is just an object. You have to tell what property to load out of it's collection. You can check PropertyNames property to see what's in the result object. Check out: http://msdn.microsoft.com/en-us/library/system.directoryservices.resultpropertycollection.aspx
for a reference of the object you are working with.

Categories