how to generate if condition block by CodeDOM or LinqExpression Dynamiclly? - codedom

if ((x == 1 && y == "test") || str.Contains("test"))
...
how to generate condition in the if block by CodeDOM or LinqExpression Dynamiclly with C#?

To allow you to use pure CodeDom for creating the expression you can use the CodeBinaryOperationExpression inside the CodeConditionStatement.
The condition would look like this:
CodeExpression condition = new CodeBinaryOperatorExpression(
new CodeBinaryOperatorExpression(
new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("x"), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(1)),
CodeBinaryOperatorType.BooleanAnd,
new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("y"), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression("test"))),
CodeBinaryOperatorType.BooleanOr,
new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("str"), "Contains"), new CodePrimitiveExpression("test")));
So then all you need are the true, and optionally false, statements:
CodeStatement[] trueStatements = { new CodeCommentStatement("Do this if true") };
CodeStatement[] falseStatements = { new CodeCommentStatement("Do this is false") };
Then put it all together in the if statement:
CodeConditionStatement ifStatement = new CodeConditionStatement(condition, trueStatements, falseStatements);
The complete sample to create a class with a method that performs the evaluation could look like this:
CodeCompileUnit compileUnit = new CodeCompileUnit();
CodeNamespace exampleNamespace = new CodeNamespace("StackOverflow");
CodeTypeDeclaration exampleClass = new CodeTypeDeclaration("GeneratedClass");
CodeMemberMethod method = new CodeMemberMethod();
method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
method.Name = "EvaluateCondition";
method.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(int)), "x"));
method.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(string)), "y"));
method.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(string)), "str"));
CodeExpression condition = new CodeBinaryOperatorExpression(
new CodeBinaryOperatorExpression(
new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("x"), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(1)),
CodeBinaryOperatorType.BooleanAnd,
new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("y"), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression("test"))),
CodeBinaryOperatorType.BooleanOr,
new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("str"), "Contains"), new CodePrimitiveExpression("test")));
CodeStatement[] trueStatements = { new CodeCommentStatement("Do this if true") };
CodeStatement[] falseStatements = { new CodeCommentStatement("Do this if false") };
CodeConditionStatement ifStatement = new CodeConditionStatement(condition, trueStatements, falseStatements);
method.Statements.Add(ifStatement);
exampleClass.Members.Add(method);
exampleNamespace.Types.Add(exampleClass);
compileUnit.Namespaces.Add(exampleNamespace);
Generate source output in C# using this code...
string sourceCode;
using (var provider = CodeDomProvider.CreateProvider("csharp"))
using (var stream = new MemoryStream())
using (TextWriter writer = new StreamWriter(stream))
using (IndentedTextWriter indentedWriter = new IndentedTextWriter(writer, " "))
{
provider.GenerateCodeFromCompileUnit(compileUnit, indentedWriter, new CodeGeneratorOptions()
{
BracingStyle = "C"
});
indentedWriter.Flush();
stream.Seek(0, SeekOrigin.Begin);
using (TextReader reader = new StreamReader(stream))
{
sourceCode = reader.ReadToEnd();
}
}
...sourceCode will contain:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace StackOverflow
{
public class GeneratedClass
{
public void EvaluateCondition(int x, string y, string str)
{
if ((((x == 1)
&& (y == "test"))
|| str.Contains("test")))
{
// Do this if true
}
else
{
// Do this if false
}
}
}
}
Change the provider from csharp to vb to get this:
'------------------------------------------------------------------------------
' <auto-generated>
' This code was generated by a tool.
' Runtime Version:4.0.30319.42000
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------
Option Strict Off
Option Explicit On
Namespace StackOverflow
Public Class GeneratedClass
Public Sub EvaluateCondition(ByVal x As Integer, ByVal y As String, ByVal str As String)
If (((x = 1) _
AndAlso (y = "test")) _
OrElse str.Contains("test")) Then
'Do this if true
Else
'Do this if false
End If
End Sub
End Class
End Namespace

To create the code above dynamical using codedom you need to do the following:
Create a Method which can be added to a class:
CodeMemberMethod method = new CodeMemberMethod();
method.Name = "TestMethod";
method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
Create a If command including statement inside the brackets eg {Value = 4;}:
CodeConditionStatement codeIf = new CodeConditionStatement(new
CodeSnippetExpression("(x == 1 && y == \"test\")|| str.Contains(\"test\")"), new
CodeSnippetStatement("value = 4;"));
Add the If command to the method which was created above:
method.Statements.Add(codeIf);

Related

How to logically iterate through records using DataReader and then do something at each iteration

Please bear with me as I'm rather new to this.
I'm trying to iterate through a recordset using DataReader object, and do something at each iteration, but it doesn't seem to be getting passed the first record. I essentially want to look at each record and then assign a specific image to a specified location based on the data in a column in the recordset.
I'm using Visual Studio 2019 and using OleDataReader to read Access table. I've got the command object working properly, just can't seem to get passed the first record.
this is the method definition in my class
//Giving string variable names to the virtual paths of the images
private string whitePallet = "~/images/White.jpg";
private string redPallet = "~/images/Red.jpg";
private string bluePallet = "~/images/Blue.jpg";
private string blackPallet = "~/images/Black.jpg";
private string greenPallet = "~/images/Green.jpg";
private string racetrack = "~/images/Racetrack.jpeg";
public string Racetrack { get => racetrack; set => racetrack = value; }
public string OpenConnection(string connectString, String selectString)
{
using (OleDbConnection cn = new OleDbConnection(connectString))
{
cn.Open(); //Open the connection.
OleDbCommand cmd = new OleDbCommand(selectString, cn);
OleDbDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
string pallet;
if (reader["Status"].ToString() == "Blocked Location")
{
pallet = blackPallet;
return pallet;
}
if (reader["Status"].ToString() == "AP Purge")
{
pallet = redPallet;
return pallet;
}
if (reader["Status"].ToString() == "Open")
{
pallet = whitePallet;
return pallet;
}
if (reader["Status"].ToString() == "Order Complete")
{
pallet = greenPallet;
return pallet;
}
if (reader["Status"].ToString() == "Pallet Full")
{
pallet = bluePallet;
return pallet;
}
}
//Close the reader and the related connection.
reader.Close();
return null;
}
}
this is the instantiation of the class/method
'''
Definitions defs = new Definitions();
Image imgRacetrack = Image.FromFile(Server.MapPath(defs.Racetrack));
//Creating image panel to draw upon using w/h of racetrack image
Image img = new Bitmap(imgRacetrack.Width, imgRacetrack.Height);
using (Graphics gr = Graphics.FromImage(img))
{
//Background image
gr.DrawImage(imgRacetrack, new Point(0, 0));
//Defining the points on the left side:
Point p1 = new Point(125, 50);
Image imgPallet = Image.FromFile(Server.MapPath(defs.OpenConnection(connectString,selectString)));
gr.DrawImage(imgPallet, p1);
Point p2 = new Point(125, 100);
Image imgPallet2 = enter code hereImage.FromFile(Server.MapPath(defs.OpenConnection(connectString, selectString)));
gr.DrawImage(imgPallet2, p2);
}
I'm expecting to iterate through each record and then place the image with the correct color into the correct position, but it's only giving the results to each point/location based on the very first record only. What's wrong with my logic??
You declare pallet string pallet; then have several if statements in which you return pallet;.
First, you want to return a string, but you're doing
pallet = blackPallet; // pallet will not be a string.
pallet = "blackPallet"; // pallet will be a string.
Second, when you say return..., you leave the method; you don't continue after that, you are saying you are done - return this value and stop.
So you need to read the value from each row and store them, then return the collection of values. Then you can loop through the collection and plug in each value as needed.
That's one way. I didn't study what you have in complete detail so there might be a better way. Hope that's enough for now.

How to pass non-optional NULL parameters to a Stored Proc using OrmLite

I'm using OrmLite against an existing SQL Server database that has published stored procedures for access. One of these SPs takes 3 int parameters, but expects that one or another will be null. However, none of the parameters are declared optional.
Here's the code I've tried:
using (IDbConnection scon = myFactory.OpenDbConnection())
{
rowCount = scon.SqlScalar<int>("EXEC myProc #FileID, #FileTypeID, #POID",
new
{
FileID = req.FileId,
FileTypeID = (int?)null,
POID = req.PoId,
});
}
But this produces a SqlException: Must declare the scalar variable "#FileTypeID". Examining the SQLParameterCollection under the covers shows that only two parameters are being generated by OrmLite.
Is it possible to call this SP with a null parameter?
It's not supported with SqlScalar. When you look at the code then you can see that SqlScalar methods from class ServiceStack.OrmLite.OrmLiteReadExtensions execute SetParameters method responsible for adding parameters to query with second parameter(excludeNulls) equal true I don't know why- mythz should answer for this ;).
If you want to fix it then you have change all SqlScalar methods to invoke SetParameters with true and SetParameters method should look like following(must support DBNull.Value not null)
private static void SetParameters(this IDbCommand dbCmd, object anonType, bool excludeNulls)
{
dbCmd.Parameters.Clear();
lastQueryType = null;
if (anonType == null) return;
var pis = anonType.GetType().GetSerializableProperties();
foreach (var pi in pis)
{
var mi = pi.GetGetMethod();
if (mi == null) continue;
var value = mi.Invoke(anonType, new object[0]);
if (excludeNulls && value == null) continue;
var p = dbCmd.CreateParameter();
p.ParameterName = pi.Name;
p.DbType = OrmLiteConfig.DialectProvider.GetColumnDbType(pi.PropertyType);
p.Direction = ParameterDirection.Input;
p.Value = value ?? DBNull.Value; // I HAVE CHANGED THAT LINE ONLY
dbCmd.Parameters.Add(p);
}
}
When you change code then you can set null for parameters in the following way:
var result = db.SqlScalar<int>("EXEC DummyScalar #Times", new { Times = (int?)null });
In my opinion you can describe it as a defect on github and I can make pull request.

Retrieve string array from COM object using Classic ASP

I have a .NET class which holds a simple array of strings available via an accessor method, which looks like this;
namespace Foo.Bar {
[ComVisible(true)]
[Guid("642279A0-85D4-4c7a-AEF5-A9FAA4BE85E5")]
public class MyClass {
private string[] _myArray;
public MyClass() { }
public MyClass(string[] myArray) {
_myArray = myArray;
}
public string[] MyArray {
get { return _myArray; }
}
}
}
I consume this class using Classic ASP;
Dim foo
Set foo = Server.CreateObject("Foo.Bar.MyClass")
if IsArray(foo.MyArray) then Response.Write("IsArray") & "<br />"
Response.Write(typename(foo.MyArray)) & "<br />"
Response.Write(UBound(foo.MyArray)) & "<br />"
This results in;
IsArray
String()
1
However, when I try to access the contents of the array using;
Response.Write(foo.MyArray(0)) & "<br />"
I get;
Microsoft VBScript runtime (0x800A01C2) Wrong number of arguments or
invalid property assignment: 'MyArray'
Any help is much appreciated.
Edit This is to provide more information after digesting the answers given (thanks)
When changing the implementation of the MyArray property to;
public object[] MyArray {
get { return (object[])_myArray; }
}
I then get the following error,
Microsoft VBScript runtime (0x800A000D) Type mismatch: 'MyArray'
So I tried individually casting each string to an object;
public object[] MyArray {
get {
object[] tmp = new object[_myArray.Count()];
for (int x = 0; x < _myArray.Count(); x++) {
tmp[x] = (object)_myArray[x];
}
return tmp;
}
}
Then I'm back to,
Microsoft VBScript runtime (0x800A01C2) Wrong number of arguments or
invalid property assignment: 'MyArray'
Edit Final solution with help from How to correctly marshal VB-Script arrays to and from a COM component written in C#
C#
public object MyArray {
get { return _myArray.Cast<object>().ToArray(); }
}
VBScript
Dim foo
Set foo = Server.CreateObject("Foo.Bar.MyClass")
bar = foo.MyArray
Response.Write bar(0)
The key was to expose object rather than object[] and as AnthonyWJones suggested, assign the array to a variable before using it.
Thanks again.
The problem is VBScript cannot actually use an array of String. It can only use an array of Variant.
Try changing MyClass to expose an object[] instead.
In addition to Anthony's suggestion I'm not sure is it the best way but in the past I used a code similar to the following to handle one dimensional arrays.
public object MyArray(int ix = -1){
string[] tmp = new string[] {"one", "two", "3", "4"};
return (ix == -1) ? (object)tmp : tmp[ix];
}
In ASP:
Response.Write(TypeName(foo.MyArray)) 'string()
Response.Write(TypeName(foo.MyArray(0))) 'string
VBScript doesn't understand generic collections such as List<string> and it doesn't understand string arrays either.
I wrote a public function into my Interface class to convert any generic collections into an ArrayList
public ArrayList toArrayList(IEnumerable collection)
{
var arrayList = new ArrayList();
foreach (object element in collection)
{
arrayList.Add(element);
}
return arrayList;
}
This code can then be used in VBScript like this
dim connector
set connector = model.getRelationByID(connectorID)
'get the related elements
dim relatedElements
set relatedElements = model.toArrayList(connector.relatedElements)
addRelatedElementoAutoDiagram relatedElements(0), relatedElements(1), model
The advantage of this approach is that I don't need to change the signature of any of the methods or properties in C#, but I can still use them in VBScript
This code demonstrates how to handle arrays between COM and ASP:
<% #Language="VBScript" %>
<% Option Explicit %>
<%
Dim tcs
Dim rc
Dim vntInput(0,4)
Dim i
vntInput(0,0) = Request.QueryString("strUser")
vntInput(0,1) = Request.QueryString("intCreate")
vntInput(0,2) = Request.QueryString("intDelete")
vntInput(0,3) = Request.QueryString("intModify")
vntInput(0,4) = Request.QueryString("intView")
Set tcs = Server.CreateObject("TestCases.ArrayFailure")
rc = tcs.AcceptArray(vntInput)
For i = 0 to UBound(vntInput, 2)
Response.write "Loop Count " & i & " " & vntInput(0,i) & "<BR>"
Next
%>
Here's a link to the article where I found this code:
http://202.102.233.250/b2000/ASP/articles/component/pv990826.htm

How do I dynamically instantiate a class, and set a property at runtime in Flex 3?

Using org.as3commons.reflect I can look-up the class name, and instantiate a class at runtime. I also have (non-working) code which invokes a method. However, I really want to set a property value. I'm not sure if properties are realized as methods internally in Flex.
I have a Metadata class which stores 3 pieces of information: name, value, and type (all are strings). I want to be able to loop through an Array of Metadata objects and set the corresponding properties on the instantiated class.
package com.acme.reporting.builders
{
import com.acme.reporting.model.Metadata;
import mx.core.UIComponent;
import org.as3commons.reflect.ClassUtils;
import org.as3commons.reflect.MethodInvoker;
public class UIComponentBuilder implements IUIComponentBuilder
{
public function build(metadata:Array):UIComponent
{
var typeClass:Class = ClassUtils.forName(getTypeName(metadata));
var result:* = ClassUtils.newInstance(typeClass);
for each (var m:Metadata in metadata)
{
if (m.name == "type")
continue;
// Attempting to invoke as method,
// would really like the property though
var methodInvoker:MethodInvoker = new MethodInvoker();
methodInvoker.target = result;
methodInvoker.method = m.name;
methodInvoker.arguments = [m.value];
var returnValue:* = methodInvoker.invoke(); // Fails!
}
return result;
}
private static function getTypeName(metadata:Array):String
{
if (metadata == null || metadata.length == 0)
throw new ArgumentError("metadata is null or empty");
var typeName:String;
// Type is usually the first entry
if (metadata.length > 1 && metadata[0] != null && metadata[0].name == "type")
{
typeName = metadata[0].value;
}
else
{
var typeMetadata:Array = metadata.filter(
function(element:*, index:int, arr:Array):Boolean
{
return element.name == "type";
}
);
if (typeMetadata == null || typeMetadata.length != 1)
throw new ArgumentError("type entry not found in metadata");
typeName = typeMetadata[0].value;
}
if (typeName == null || typeName.length == 0)
throw new Error("typeName is null or blank");
return typeName;
}
}
}
Here's some usage code:
var metadata:Array = new Array();
metadata[0] = new Metadata("type", "mx.controls.Text", null);
metadata[1] = new Metadata("text", "Hello World!", null);
metadata[2] = new Metadata("x", "77", null);
metadata[3] = new Metadata("y", "593", null);
this.addChild(new UIComponentBuilder().build(metadata));
I realize that I have to declare a dummy variable of the type I was to instantiate, or use the -inculde compiler directive. An unfortunate drawback of Flex.
Also, right now there's code to account for typecasting the value to it's specified type.
Dynamic execution in AS3 is much simpler than in other languages. This code:
var methodInvoker:MethodInvoker = new MethodInvoker();
methodInvoker.target = result;
methodInvoker.method = m.name;
methodInvoker.arguments = [m.value];
var returnValue:* = methodInvoker.invoke(); // Fails!
can be simplified to this:
var returnValue:* = result[method](m.value);
EDIT:
Since it's a property, it would be done like this:
result[method] = m.value;
and there is no return value (well, you can call the getter again but it should just return m.value unless the setter/getter do something funky.

Flex AIR Sqlite as embedded database

I am devloping an editor with spell check feature in Flex + AIR using Sqlite as an embedded database.
In database I have a table with the words. The table contains lacs of words. I have written the code to search for the word in table. I have used the sync method for all this. This pauses my application while searching.
I would like to use async method to stop application pause.
The code for Search word is as follows:
public function searchInDictionary(word:String):Boolean
{
if(word == "")
return true;
connection= new SQLConnection();
var query:SQLStatement = new SQLStatement();
query.sqlConnection = connection;
query.text = 'SELECT id FROM tbl_'+ CommonLanguageCode +' WHERE word LIKE "'+word+'"';
try
{
connection.open( dbfile,SQLMode.READ );
}
catch(ex:Error)
{}
if(!connection.connected)
connection.open( dbfile );
query.execute();
var result:SQLResult = query.getResult();
if( result.data == null )
{
return false;
}
else
{
var numRows:uint = result.data.length;
var id:String;
if(numRows>0)
return true;
return false;
}
return false;
}
If this function returns false(word not found) then i have to call the function to to red underline that word.
Please suggest me if I am going wrong. As I am using Sync method & it takes some milli seconds to search a word. & if I am wrting a paragraph then it makes my application sluggish.
Is there any other way I can store the words & search more fastly. If yes then please et me know.
Thanks in advance.
You'll need to reorganize your code a bit so that you call your function that does the red underline when the results come back instead of returning a Boolean from the method.
public function searchInDictionary(word:String):void
{
// Don't bother searching if no word was passed in
if(word == "") return;
// Open db connection asynchronously
var connection:SQLConnection = new SQLConnection();
connection.openAsync(dbfile, SQLMode.READ);
// Create statement
var statement:SQLStatement = new SQLStatement();
statement.sqlConnection = connection;
statement.text = 'SELECT id '
+ 'FROM tbl_'+ CommonLanguageCode +' '
+ 'WHERE word LIKE "' + word + '"';
// Add event listener
statement.addEventListener(SQLEvent.RESULT, function(event:SQLEvent):void{
var result:SQLResult = event.target.getResult();
if(result.data.length == 0) {
// ... call method to red underline word in text ...
}
});
// Execute statement
statement.execute();
}

Resources