Compile C# Roslyn - c#

I'm using Roslyn to try and compile and run code at runtime. I've ysed some code I found online and have it somewhat working.
public Type EvalTableScript(string Script, CRMMobileFramework.EnbuUtils EnbuUtils, CRMMobileFramework.Includes.DBAdapter dbConn)
{
var syntaxTree = SyntaxTree.ParseText(Script);
var compilation = Compilation.Create("EnbuScript.dll",
options: new CompilationOptions(outputKind: OutputKind.DynamicallyLinkedLibrary),
references: new[]
{
new MetadataFileReference(typeof(object).Assembly.Location),
new MetadataFileReference(typeof(EnbuUtils).Assembly.Location),
new MetadataFileReference(typeof(DBAdapter).Assembly.Location),
MetadataFileReference.CreateAssemblyReference("System.Data"),
MetadataFileReference.CreateAssemblyReference("System.Linq"),
MetadataFileReference.CreateAssemblyReference("System"),
MetadataFileReference.CreateAssemblyReference("System.XML")
},
syntaxTrees: new[] { syntaxTree });
var diagnostics = compilation.GetDiagnostics();
foreach (var diagnostic in diagnostics)
{
Console.WriteLine("Error: {0}", diagnostic.Info.GetMessage());
}
Assembly assembly;
using (var stream = new MemoryStream())
{
EmitResult emitResult = compilation.Emit(stream);
assembly = Assembly.Load(stream.GetBuffer());
}
Type ScriptClass = assembly.GetType("EnbuScript");
// Pass back the entire class so we can call it at the appropriate time.
return ScriptClass;
}
Then I'm trying to call this:
string Script = #"
using System;
using System.Data;
using System.IO;
using System.Linq;
public class EnbuScript
{
public string PostInsertRecord(CRMMobileFramework.EnbuUtils EnbuUtils,CRMMobileFramework.Includes.DBAdapter dbConn)
{
string ScriptTable = ""QuoteItems"";
DataSet EntityRecord = dbConn.FindRecord(""*"", ScriptTable, ""QuIt_LineItemID='"" + EnbuUtils.GetContextInfo(ScriptTable) + ""'"", """", 1, 1, false);
string OrderId = EntityRecord.Tables[""item""].Rows[0][""QuIt_orderquoteid""].ToString();
string UpdateOrderTotalCommand = ""UPDATE Quotes SET Quot_nettamt = (select SUM(QuIt_listprice * quit_quantity) from QuoteItems where quit_orderquoteid = "" + OrderId + "" ) where Quot_OrderQuoteID = "" + OrderId;
dbConn.ExecSql(UpdateOrderTotalCommand);
return ""Complete"";
}
}";
Type EnbuScript = EnbuUtils.EvalTableScript(Script, EnbuUtils, dbConn);
MethodInfo methodInfo = EnbuScript.GetMethod("InsertRecord");
object[] parameters = { EnbuUtils, dbConn };
string InsertRecordResult = methodInfo.Invoke(null, parameters).ToString();
As you can see I've been messing around with trying to pass parameters to the compilation.
Basically I've got 4 functions I need to support, that will come in as a string. What I'm trying to do is create a class for these 4 functions and compile and run them. This part works.
What I now need to be able to do is pass class instances to this. In the code you'll see a dbConn which is basically my database connection. I need to pass the instance of this to the method I'm calling at runtime so it has it's correct context.
I have another implementation of this where I'm using the Roslyn session. I originally tried to use this and override my function at runtime but that didn't work either. See below what I tried:
public static void EvalTableScript(ref EnbuUtils EnbuUtils, DBAdapter dbConn, string EvaluateString)
{
ScriptEngine roslynEngine = new ScriptEngine();
Roslyn.Scripting.Session Session = roslynEngine.CreateSession(EnbuUtils);
Session.AddReference(EnbuUtils.GetType().Assembly);
Session.AddReference(dbConn.GetType().Assembly);
Session.AddReference("System.Web");
Session.AddReference("System.Data");
Session.AddReference("System");
Session.AddReference("System.XML");
Session.ImportNamespace("System");
Session.ImportNamespace("System.Web");
Session.ImportNamespace("System.Data");
Session.ImportNamespace("CRMMobileFramework");
Session.ImportNamespace("CRMMobileFramework.Includes");
try
{
var result = (string)Session.Execute(EvaluateString);
}
catch (Exception ex)
{
}
}
I tried to call this using:
string PostInsertRecord = "" +
" public override void PostInsertRecord() " +
"{ " +
" string ScriptTable = \"QuoteItems\"; " +
"DataSet EntityRecord = dbConn.FindRecord(\"*\", ScriptTable, \"QuIt_LineItemID='\" + EnbuUtils.GetContextInfo(ScriptTable) + \"'\", \"\", 1, 1, false); " +
"string OrderId = EntityRecord.Tables[\"item\"].Rows[0][\"QuIt_orderquoteid\"].ToString(); " +
"string UpdateOrderTotalCommand = \"UPDATE Quotes SET Quot_nettamt = (select SUM(QuIt_listprice * quit_quantity) from QuoteItems where quit_orderquoteid = \" + OrderId + \" ) where Quot_OrderQuoteID = \" + OrderId; " +
"dbConn.ExecSql(UpdateOrderTotalCommand); " +
"} ";
The function is declared as a public virtual void in the EnbuUtils class but it says it doesn't have a suitable method to override.
Safe to say, I'm stumped!
Any help appreciated!
Thanks

I got this in the end - this first method was very close to what I actually needed. Changed the method to static and had to add a few references including the full namespace.

Related

upgrade issues when upgrading CSVhelper libraries from version 2.7 to 30

My application is using CSVHelper libraries of very old version 2.7.1.
Now I want to upgrade to the latest version of CSVHelper version 30.
I have installed using the NuGet package manager. When I build my application, it throws errors.
Below is the old code that throws an error.
csvReader.Configuration.HasHeaderRecord = hasHeaderRecord;
csvReader.Configuration.IgnoreBlankLines = false;
csvReader.Configuration.IgnoreReadingExceptions = true;
csvReader.Configuration.WillThrowOnMissingField = false;
csvReader.Configuration.TrimFields = true;
csvReader.Configuration.ReadingExceptionCallback =
(ex, row) =>
{
if (ex is CsvHelper.TypeConversion.CsvTypeConverterException)
{
foreach (DictionaryEntry error in ex.Data)
{
AddRowError(row.Row, error.Value.ToString() + " Column Name: '" + GetColumnName(row) + "'");
}
}
else if (ex is FormatException)
{
AddRowError(row.Row, ex.Message + " Column Name: '" + GetColumnName(row) + "' Column Value: '" + GetColumnValue(row) + "'");
}
else
{
AddRowError(row.Row, string.Format("Line[{0}]: {1}", row.Row, ex.StackTrace));
}
};
One more error about The type or namespace name 'ICsvReader' could not be found (are you missing a using directive or an assembly reference?)
Can anyone suggest how to fix these upgradation issues?
Configuration must be passed in to the CsvReader constructor. The constructor also now requires either CultureInfo or IReaderConfiguration to be passed in with the TextReader.
void Main()
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
IgnoreBlankLines = false,
ReadingExceptionOccurred = args => { Console.WriteLine(args.Exception.ToString()); return false;},
MissingFieldFound = null,
TrimOptions = TrimOptions.Trim
};
using (var reader = new StringReader("Id,Name\n1\n2,name2\nthree,name3\n4,name4"))
using (var csv = new CsvReader(reader, config))
{
var records = csv.GetRecords<Foo>().Dump();
}
}
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}

How to process a DynamoDBEvent in a C# Lambda Function?

I created a C# Lambda function that is being triggered with a DynamoDB stream. It gets excecuted just fine. However, the StreamRecord of the NewImage value returns no values. The count is 0. What am I doing wrong? I have checked all the AWS documention but this seems more and more like a bug. My below lambda function should work and it should return at least 1 StreamRecord in my example. attributeMap.Count always returns 0 but it should return 1.
public void FunctionHandler(DynamoDBEvent dynamoDbEvent, ILambdaContext context)
{
Console.WriteLine($"Beginning to process {dynamoDbEvent.Records.Count} records...");
foreach (var record in dynamoDbEvent.Records)
{
Console.WriteLine($"Event ID: {record.EventID}");
Console.WriteLine($"Event Name: {record.EventName}");
var attributeMap = record.Dynamodb.NewImage;
if (attributeMap.Count > 0) // If item does not exist, attributeMap.Count will be 0
{
Console.WriteLine(attributeMap["AccountId"].S);
}
}
Console.WriteLine("Stream processing complete.");
}
UPDATE: October, 4th, 2018. I no longer use an admin app. I now use CloudFormation exclusively to create and maintain everyting including full CI/CD pipelines with CodePipelines. This includes all Serverless lamba functions in .NET Core 2.1.
I figured it out. The sucky thing is that AWS Docs do not say anything about this. This was a pain in the ass to find out. For anyone else who might need this information here it is: You have to set the stream view type when you create the DynamoDB stream for the table. Here is a picture for the AWS Console:
However, since I setup all tables via an admin console (in C# Core 2.0), here is how I setup the setup the table including the stream specification and the event source mapping request to the lambda function:
var request = new CreateTableRequest
{
TableName = TABLE_CREATE_ACCOUNT,
AttributeDefinitions = new List<AttributeDefinition>()
{
new AttributeDefinition
{
AttributeName = "CommandId",
AttributeType = ScalarAttributeType.S
}
},
KeySchema = new List<KeySchemaElement>()
{
new KeySchemaElement
{
AttributeName = "CommandId",
KeyType = KeyType.HASH
}
},
ProvisionedThroughput = new ProvisionedThroughput
{
ReadCapacityUnits = 1,
WriteCapacityUnits = 1
},
StreamSpecification = new StreamSpecification
{
StreamEnabled = true,
StreamViewType = StreamViewType.NEW_IMAGE
}
};
try
{
var response = _db.CreateTableAsync(request);
var tableDescription = response.Result.TableDescription;
Console.WriteLine("{1}: {0} ReadCapacityUnits: {2} WriteCapacityUnits: {3}",
tableDescription.TableStatus,
tableDescription.TableName,
tableDescription.ProvisionedThroughput.ReadCapacityUnits,
tableDescription.ProvisionedThroughput.WriteCapacityUnits);
string status = tableDescription.TableStatus;
Console.WriteLine(TABLE_CREATE_ACCOUNT + " - " + status);
WaitUntilTableReady(TABLE_CREATE_ACCOUNT);
// This connects the DynamoDB stream to a lambda function
Console.WriteLine("Creating event source mapping between table stream '"+ TABLE_CREATE_ACCOUNT + "' and lambda 'ProcessCreateAccount'");
var req = new CreateEventSourceMappingRequest
{
BatchSize = 100,
Enabled = true,
EventSourceArn = tableDescription.LatestStreamArn,
FunctionName = "ProcessCreateAccount",
StartingPosition = EventSourcePosition.LATEST
};
var reqResponse =_lambda.CreateEventSourceMappingAsync(req);
Console.WriteLine("Event source mapping state: " + reqResponse.Result.State);
}
catch (AmazonDynamoDBException e)
{
Console.WriteLine("Error creating table '" + TABLE_CREATE_ACCOUNT + "'");
Console.WriteLine("Amazon error code: {0}", string.IsNullOrEmpty(e.ErrorCode) ? "None" : e.ErrorCode);
Console.WriteLine("Exception message: {0}", e.Message);
}
catch (Exception e)
{
Console.WriteLine("Error creating table '" + TABLE_CREATE_ACCOUNT + "'");
Console.WriteLine("Exception message: {0}", e.Message);
}
The key is
StreamViewType = StreamViewType.NEW_IMAGE
That was it.

C# - Visual Studio - 'object' does not contain a definition for 'ExecuteQuery'

I am trying to add code to an existing project that will check for existence of a device in SCCM and delete it if it does exist. I seem to be missing something, in that particular block of code. I get an error - 'object' does not contain a definition for 'ExecuteQuery' and no extension method 'ExecuteQuery' accepting a first argument of type 'object' could be found.
Here is the C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Net.NetworkInformation;
using SupportToolkit.Models;
using SupportToolkit.WindowsAutomationServices;
using NLog;
using System.Text;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.Management;
namespace SupportToolkit.Controllers
{
public class TPOSRecordDeletionController : Controller
{
private static Logger recordDeletionLogger = LogManager.GetCurrentClassLogger();
[Authorize(Roles = #"nor\NST_RIT_Users,nor\NST_STM_Users,nor\NST_Admin_Users,nor\NST_CORP_Users")]
// GET: TPOSRecordDeletion
public ActionResult Index(TPOSRecordDeletionModel model)
{
if (model.ComputerName != null)
{
}
if (ModelState.IsValid)
{
if (!(string.IsNullOrEmpty(model.btnDeleteRecord)))
{
InvokeRecordDeletion(model);
}
}
return View(model);
}
[Authorize(Roles = #"nor\NST_RIT_Users,nor\NST_STM_Users,nor\NST_Admin_Users,nor\NST_CORP_Users")]
public string InvokeRecordDeletion(TPOSRecordDeletionModel model)
{
model.Status = "Running Service";
var windowsAutomationService = new WindowsAutomationServicesClient();
string shortServiceOutput;
var serviceAction = "Remove-TPOSRecords";
var SCCMServer = "server.nor.net";
var siteCode = "PO60";
string[] recordDeletionArguments = new string[1];
recordDeletionArguments[0] = model.ComputerName;
model.Status = "Processing" + model.ComputerName;
Ping pingSender = new Ping();
PingOptions options = new PingOptions();
// Use the default Ttl value which is 128,
// but change the fragmentation behavior.
options.DontFragment = true;
// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 120;
PingReply reply = pingSender.Send(model.ComputerName, timeout, buffer, options);
if (reply.Status == IPStatus.Success)
{
model.Status = model.ComputerName + "is currently online and will not be removed!";
}
else
{
// set up domain context
using (var ctx = new System.DirectoryServices.AccountManagement.PrincipalContext(System.DirectoryServices.AccountManagement.ContextType.Domain))
{
// find a computer
ComputerPrincipal computer = ComputerPrincipal.FindByIdentity(ctx, model.ComputerName);
if (computer == null)
{
model.Status = model.ComputerName + "does not exist in Active Directory.";
}
else
{
computer.Delete();
model.Status = model.ComputerName + "successfully removed from Active Directory!";
}
//insert code here for checking for existence of computer in SCCM and removing from SCCM if exists
SmsNamedValuesDictionary namedValues = new SmsNamedValuesDictionary();
WqlConnectionManager connection = new WqlConnectionManager(namedValues);
connection.Connect("s0319p60.nordstrom.net");
foreach (IResultObject computerobject in connection.QueryProcessor.ExecuteQuery("Select ResourceID From SMS_R_System Where Name ='" + model.ComputerName + "'"))
{
if (computerobject == null)
{
model.Status = model.ComputerName + "does not exist in SCCM.";
}
else
{
computerobject.Delete();
model.Status = model.ComputerName + "successfully removed from SCCM!";
}
}
}
var userName = User.Identity.Name;
var serviceOutput = windowsAutomationService.RunAutomationService(serviceAction, userName, recordDeletionArguments);
recordDeletionLogger.Info(userName + " is attempting to remove the record " + model.ComputerName);
if (serviceOutput.Length >= 7)
{
shortServiceOutput = serviceOutput.Substring(0, 7);
shortServiceOutput = shortServiceOutput.ToLower();
}
else
{
shortServiceOutput = serviceOutput;
shortServiceOutput = shortServiceOutput.ToLower();
}
if (shortServiceOutput == "success")
{
model.Status = "Successfully removed " + model.ComputerName + " from SCCM and Active Directory";
recordDeletionLogger.Info(userName + " successfully removed " + model.ComputerName + " from SCCM and Active Directory");
return "Success";
}
else
{
model.Status = "Failure removing " + model.ComputerName + " from SCCM and Active Directory. Unknown Error";
recordDeletionLogger.Info(userName + " failed to remove " + model.ComputerName + " from SCCM and Active Directory");
return "Failure";
}
}
}
}
internal interface IResultObject
{
//void Delete();
void Delete();
}
internal class WqlConnectionManager
{
private SmsNamedValuesDictionary namedValues;
public WqlConnectionManager(SmsNamedValuesDictionary namedValues)
{
this.namedValues = namedValues;
}
public object QueryProcessor { get; internal set; }
internal void Connect(string v)
{
throw new NotImplementedException();
}
public object ExecuteQuery { get; internal set; }
}
internal class SmsNamedValuesDictionary
{
public SmsNamedValuesDictionary()
{
}
}
Well - after a few days of searching, I FINALLY figured out the issue.
The entire block of code at the end, was not necessary. The issue was that there were missing assembly references and using statements in the code. Specifically:
using Microsoft.ConfigurationManagement.ManagementProvider;
using Microsoft.ConfigurationManagement.ManagementProvider.WqlQueryEngine;
The corresponding DLLs also needed to be added to the project as references.
I hope this helps someone who runs into similar issues and is not versed in C# coding. I myself am only versed in PowerShell, so figuring this out took a lot of work for me. Thanks.
You create a custom "connection" object:
WqlConnectionManager connection = new WqlConnectionManager(namedValues);
Then call a method on one of its properties:
connection.QueryProcessor.ExecuteQuery("...")
But what is that QueryProcessor property?...
public object QueryProcessor { get; internal set; }
It's an object. As the error states, object doesn't have a method called ExecuteQuery. (It doesn't have very many methods or properties at all, really.)
I can't really tell from this code (maybe I'm missing something?) what specific type you're expecting QueryProcessor to be, but it should definitely be something more specific than object. Something analogous to a SQLCommand object, perhaps? Essentially, whatever type would have that ExecuteQuery method.
If there's a compelling reason in the existing codebase for this to be of type object, you'd need to determine what that reason is. There seems to be a lot of use of object here, which smells of some bad design choices from before you got there.

Create PowerShell PSObject in C# cmdlet

New to C# but experienced in PowerShell. Taking over someone else's code. Writing a compiled PowerShell module, and trying to figure out how to create an object based on returned data. Right now, code returns a string:
ServerResponse<UCCSiteModel> requestModel = this.MakeRequest("/site/api/", "site", "GET", this.Credentials, queryString);
StringBuilder builder = new StringBuilder();
if (requestModel != null && requestModel.Response != null)
{
builder.AppendLine("SiteID: " + requestModel.Response.SiteID);
builder.AppendLine("Identity: " + requestModel.Response.SiteName);
builder.AppendLine("Site Code: " + requestModel.Response.SiteCode);
builder.AppendLine("Contact Name: " + requestModel.Response.ContactName);
builder.AppendLine("Contact Number: " + requestModel.Response.ContactNumber);
builder.AppendLine("Contact Email Address: " + requestModel.Response.ContactEmailAddress);
builder.AppendLine("Address: " + requestModel.Response.Address);
builder.AppendLine("City: " + requestModel.Response.City);
builder.AppendLine("State: " + requestModel.Response.State);
builder.AppendLine("Post Code: " + requestModel.Response.PostCode);
builder.AppendLine("Time Zone: " + requestModel.Response.Timezone);
builder.AppendLine("Longitude: " + requestModel.Response.longitude);
builder.AppendLine("Latitude: " + requestModel.Response.latitude);
this.WriteResponse(requestModel, builder.ToString());
}
How do I create an object from requestModel.Response to send back to PowerShell instead of the string? When writing PowerShell, I would normally use New-Object PsObject, and then Add-Member. Not sure how to do that in C#, or what it's called (so I can search). Anyone?
You can mirror the behavior of Add-Member simply by calling Add() on the Members property of your PSObject (I would change the property names to CamelCase for ease of accessibility in PowerShell):
if (requestModel != null && requestModel.Response != null)
{
PSObject responseObject = new PSObject();
responseObject.Members.Add(new PSNoteProperty("SiteID", requestModel.Response.SiteID));
responseObject.Members.Add(new PSNoteProperty("Identity", requestModel.Response.SiteName));
// and so on etc...
responseObject.Members.Add(new PSNoteProperty("Latitude", requestModel.Response.latitude));
this.WriteObject(responseObject);
}
Without knowing all the details I can't say this is the best plan, but here is what I would do. You could define a class and then return it. So you would create a new class such as below:
public class RequestResponse {
public int SiteID { get; set;}
public string Identity { get; set; }
other fields...
}
Next, in the code you posted, you would create the object and then fill the properties of the class.
var response = new RequestResponse();
if (requestModel != null && requestModel.Response != null)
{
response.SiteID = requestModel.Response.SiteID;
response.Identity = requestModel.Response.Identity ;
fill in other fields...
this.WriteResponse(requestModel, response);
}
I hope this gets you started in the right direction.
Wade

Testing C# method, SQL involved

I have a method here I created for homework. I believe it works, and I want to test it to work. So here's the method:
public static bool UpdatePerson (Personnel person, out string result)
{
result = "update not successful";
bool flag = false;
System.Data.SqlClient.SqlCommand updatePerson = new System.Data.SqlClient.SqlCommand();
updatePerson.Connection = Data.con;
//updatePerson.CommandType = CommandType.StoredProcedure;
SqlParameter p1 = new SqlParameter("perFirstName", person.First);
SqlParameter p2 = new SqlParameter("perMiddleName", person.Middle);
SqlParameter p3 = new SqlParameter("perLastName", person.Last);
SqlParameter p4 = new SqlParameter("ID", person.PersonnelID);
updatePerson.Parameters.Add(p1);
updatePerson.Parameters.Add(p2);
updatePerson.Parameters.Add(p3);
updatePerson.Parameters.Add(p4);
updatePerson.CommandText = "Update tblPersonnel Set perFirstName = " + p1 + " perMiddleName = " + p2 + " perLastName = " + p3 + "Where ID = " + p4;
try
{
Data.con.Open();
updatePerson.ExecuteNonQuery();
result = "Update Successful";
flag = true;
}
catch (Exception ex)
{
result = ex.Message;
}
finally
{
if (Data.con.State == System.Data.ConnectionState.Open)
Data.con.Close();
}
return flag;
}
Now here's the test coding:
using MovieLibrary;
namespace Test
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string result = " ";
MovieLibrary.Personnel update = MovieLibrary.Personnel.UpdatePerson(MovieLibrary.Personnel, out result);
if (update != null)
this.Label1.Text = result;
}
}
The only thing giving me a problem is inserting data in the parameters. This gives me an error saying MovieLibrary.Personnel is a type and does not belong there.
You need to pass an instance of Personnel. Currently, you're using the type's name as an argument.. which you cannot do. Also.. your method returns bool.. not a Personnel object.
Hopefully this makes it more clear. This is what you have, which is wrong:
MovieLibrary.Personnel update =
MovieLibrary.Personnel.UpdatePerson(MovieLibrary.Personnel, out result);
// ^^^^^^^^^^^^^^^^^^^^^^ Wrong
However, this is what you need:
Personnel p = new Personnel();
// set properties here
// it returns bool
bool updated =
MovieLibrary.Personnel.UpdatePerson(p, out result);
// ^^ Right.. an instance
Also, you are returning bool, but also have an out parameter for the result. Consider making it return the result (or deciding what to do based on the boolean return value.. or possibly let the exception get thrown out of the method).

Categories