As a headsup, I'm new to stackoverflow so please do tell me if I'm just bad at searching for a solution.
I have a class library that's able to read csv files depending on the public members of a class and then assign them. However, I don't know a way to avoid a long switch or if else when I want to call/invoke the method.
Here's how I currently use it:
public class DataSetup
{
List<object> ObjList = new List<object>();
public DataSetup(string file)
{
switch (file)
{
case "Persons.csv":
AssignCsvData<Person>(file);
break;
case "Address.csv":
AssignCsvData<Address>(file);
break;
}
}
public void AssignCsvData<T>(string file) where T : CsvableBase, new()
{
var cr = new CsvReader<T>();
var csvObjs = cr.Read(file);
foreach (var obj in csvObjs)
{
ObjList.Add(obj);
}
}
}
The switch is not "done", since I'm hoping for a better way.
So is there a way to call the AssignCsvData without the switch?
So far I've tried taking a type as a parameter for the constructor, but I can't use it as a type when it's a variable.
If the name of the file always represents a class. In your case there is a mismatch between Persons.csv and Person, but say they do match then the following could work using reflection, rather than a switch statement:
var type = Type.GetType(Path.GetFileNameWithoutExtension(file));
var method = typeof(Processor)
.GetMethod("AssignCsvData")
.MakeGenericMethod(type);
method.Invoke(new Processor(), file);
NOTE: If you'd like to test this in LinqPad, don't forget to use the "UserQuery+" namespace to get the type. i.e.:
var type = Type.GetType($"UserQuery+{Path.GetFileNameWithoutExtension(file)}");
Related
I am making a windows application.
At first I declared var and it contains another class method.
var ExtList = ExtTarget.GetExtTargets();
And GetExtTargets() is like this
public static List<ExtTarget> GetExtTargets()
{
var dt = SqlHelper.ExecuteDataTable(QueryHelper.ConnectionString,
#"
SELECT [seq],[SourceKind],[ExtKind],[DBKind],[ConnectionString]
,[FilePath],[TableName],[FileKind],[RowSplitter],[ColumnSplitter]
,[Title],[GroupName],[SyncOrder],[RepeatKind],[RepeatMonth]
,[RepeatDay],[RepeatHour],[RepeatMin],[RepeatWeek],[RepeatWeekNum]
,[LastSyncExecDate]
FROM [ExtTarget]
order by GroupName,SyncOrder");
return dt.Rows.Cast<DataRow>().Select<DataRow, ExtTarget>(a => ExtTarget.RowToModel(a)).ToList();
}
Then, I used it to foreach and then I want to pass Ext to another method's parameter.
Code is like this.
public void ProcessExtSync(object obj)
{
while (IsProcessGoing)
{
Thread.Sleep(ThreadDelay);
if (!IsProcessGoing) return;
var ExtList = ExtTarget.GetExtTargets();
foreach (var Ext in ExtList) // I want to use this Ext as parameter
{
while (IsSourceSyncGoing)
{
Thread.Sleep(ThreadDelay);
}
IsExtSyncGoing = true;
bool ExtSyncForceToRun = ConfigSettingHelper.Instance.IsServiceConfig(Words.ExtSyncForceToRun);
bool ExtSyncForceToRunOnlyError = ConfigSettingHelper.Instance.IsServiceConfig(Words.ExtSyncForceToRunOnlyError);
bool ExtSyncNeedToRun = ConfigSettingHelper.Instance.GetNextExecutingTime(Ext) < DateTime.Now;
if (ExtSyncForceToRun || ExtSyncNeedToRun)
{
//I want to pass Ext as parameter to this method
ServiceProcess.Instance.SyncExt();
if (ExtSyncForceToRun)
{
ConfigSettingHelper.Instance.SetServiceConfig(Words.ExtSyncForceToRun, false);
}
if (ExtSyncForceToRunOnlyError)
{
ConfigSettingHelper.Instance.SetServiceConfig(Words.ExtSyncForceToRunOnlyError, false);
}
}
if (!IsProcessGoing) return;
}
IsExtSyncGoing = false;
}
}
How can I modify that code? Please help me.
var is just a shortcut way of implicitly typing a variable. It saves some typing, but sometimes makes code harder to read when the reader can't determine the type. The compiler can figure out the strong type, though (or you'll get a compiler error), and if you hover over it in Visual Studio, the compiler will tell you the actual type.
With that out of the way, all you need to do is make sure that the method you want to pass your variable to takes in the type that you want to pass it (remember the type is not var, but in your case it is an ExtTarget).
The method you're calling should have a signature similar to this (although it may return any type):
public void SyncExt(ExtTarget extTarget)
{
// Implementation code here
}
Then in your code above you can call:
ServiceProcess.Instance.SyncExt(Ext);
I'm trying to write a code analysis rule with roslyn.
Basically, I have to check whether an each of arguments which a Microsoft.Practices.Prism.Commands.DelegateCommand() is created is wrapped in try catch or not.
The main idea is collect all ObjectCreationExpressionSyntax objects of DelegateCommand class and check each constructor's argument if the first StatementSyntax is TryStatementSyntax or not.
Can you help me with getting all StatementSyntax from ArgumentSyntax ? Or may be you have an another approach ?
public IEnumerable<IdentifierInfo> Collect(SyntaxNode rootNode, SemanticModel semanticModel)
{
ObjectCreationExpressionSyntax[] objCreation = rootNode
.DescendantNodes()
.OfType<ObjectCreationExpressionSyntax>()
.Where(c=>(c.Type as IdentifierNameSyntax)?.Identifier.Value.ToString() == "DelegateCommand")
.ToArray();
foreach (var obj in objCreation)
{
var args = obj.ArgumentList.Arguments;
foreach (ArgumentSyntax arg in args)
{
var expession = arg.Expression;
var symbol = semanticModel.GetSymbolInfo(expession).Symbol as IMethodSymbol;
}
}
}
Bellow you can find what I actually compile for searching through:
public class Program
{
public delegate void MyDelegate();
public static void DelegateMethod() { try { } catch { } }
public static void Main(string[] args)
{
DelegateCommand del1 = new DelegateCommand(() => {try{}catch{}});
DelegateCommand del2 = new DelegateCommand(new Action(() => { }));
DelegateCommand del3 = new DelegateCommand(DelegateMethod);
var barInit = (Action)(DelegateMethod);
DelegateCommand del4 = new DelegateCommand(barInit);
ICommand test;
test = new Microsoft.Practices.Prism.Commands.DelegateCommand(() => { });
}
}
You start in a good way, but to handle it completely, its required more work.
Lets see in your example what we have
(The screenshot is from LINQ debugging feature from OzCode)
Here what I wrote is
var argsExpr = objCreation.Select(o => o.ArgumentList.Arguments.First())
As you can see in the right side of the window, we have a three types of syntax nodes in the arguments, so we don't have a general way to handle them all.
You have two ways to handle it.
Write method that get SyntaxNode and according to its type, check if the first statement is a try\catch statement
Write SyntaxWalker and visit relevant methods, and there, check if the first statement is a try\catch statement
For example to handle the first case which is ParenthesizedLambdaExpressionSyntax you need to write something like this (or by yourself or by overriding the appropriate Visit method of the SyntaxWalker)
public static bool IsTryStatement(ParenthesizedLambdaExpressionSyntax node)
{
return ((BlockSyntax) node.Body).Statements.First() is TryStatementSyntax;
}
This is just an example. In your real code you need to handle all cases.
For the IdentifierNameSyntax you need to get the method symbol first:
semanticModel.GetSymbolInfo(identifier).Symbol
Then you need to get the syntax node from DeclaringSyntaxReferences and use span, or you can use location of the symbol or any other way (ConstructFrom maybe).
my goal is to write a class that can compile/serialize any piece of code. I am thinking of some ideas and it's just a concept, I don't expect you to write entire solution, I am thinking of best way to do that.
I thought about two ideas:
Create an interface in compiler class, then read interface method body and compile it into separate assembly using roslyn.
Serialize expression tree.
AD 1. First concept
public class Runnable : IRunnable
{
void Run();
}
public interface ICodeRunner
{
void RunCode<T>() where T : IRunnable
}
public class CodeRunner : ICodeRunner
{
public void RunCode<T>() where T : IRunnable
{
MethodInfo mi = typeof(T).GetMethod("Run");
MethodBody mb = mi.GetMethodBody();
// somehow get method body if possible and compile it using roslyn.
// like this:
// string syntaxTree = mb.GetMethodBodyToString() // this method doesn't exist. There must be a way to do that
// CSharpCompilation compilation = CSharpCompilation.Create(
// "abc",
// new[] { syntaxTree },
// new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) },
// new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
// using (var dllStream = new FileStream(#"abc.dll", FileMode.OpenOrCreate))
// using (var pdbStream = new FileStream(#"abc.pdb", FileMode.OpenOrCreate))
// {
// var emitResult = compilation.Emit(dllStream, pdbStream);
// if (!emitResult.Success)
// {
// // emitResult.Diagnostics
// }
// }
}
}
The alternative is to serialize lambda as expression tree into a file, I saw some libraries to do that.
Are there any easier ways to this?
My general use case is to create a class that can serialize some code (logic) and payload into two separate files, send it over the wire and run piece of code + payload on a different machine. The interface is exe file and input file which is sent over the wire. Are there any ready solution for this problem?
Background:
Bit of a strange one. Using SSIS to deal with data coming from an API, my first script gets the data and puts it against a strongly typed model called "TestModel"
The SSIS package then saves the resulting information to a variable which is then passed into the next part of the SSIS package. Being SSIS name spaces are not carried around so each script task is in isolation.
Script Task One Namespace: ST_a048e0de86e1432d8da6f60b5d7055db
Script Task Two Namespace: SC_0573a66ec6c0486a98ee00cea4365654
The Issue:
The second SSIS script task picks up the variable and reads it fine, I can in debug see all my rows and all the relevant data. Now things start getting weird, the type of the List when it reaches my second script
object {System.Collections.Generic.List<ST_a048e0de86e1432d8da6f60b5d7055db.TestModel>}
Each of the objects in this Generic.List has the Type of:
ST_a048e0de86e1432d8da6f60b5d7055db.TestModel
What can I do with this list? Pretty much nothing...
I have duplicated the TestModel.cs from script namespace one into namespace two hoping that it would just match on nicely if I created a new list and passed the object too it, alas no.
What I have tried so far:
IEnumerable e = data as List<TestModel>; //Returns 0 rows
IEnumerable list = (IEnumerable)data; // returns all rows, type System.Collections.IEnumerable {System.Collections.Generic.List<ST_a048e0de86e1432d8da6f60b5d7055db.TestModel>}
List<TestModel> listtest = ((TestModel[])data).ToList() // Runtime error
List<TestModel> listtest2 = list.Cast<TestModel>().ToList(); //Runtime error - Unable to cast type 'ST_a048e0de86e1432d8da6f60b5d7055db.TestModel' to type 'SC_0573a66ec6c0486a98ee00cea4365654.TestModel'
My end goal is that I need something I can loop through and manipulate into an object SSIS can digest. That part is easy but getting it to loop is proving very difficult!
Extra Note: SSIS Packages are really annoying with dependencies so really looking to avoid using anything special. Also the name spaces are 100% isolated from on another no communication between the two is possible.
Try using Enumerable.OfType Method (IEnumerable)
List<TestModel> list = ((IEnumerable)data).OfType<TestModel>().ToList();
UPDATE:
IEnumerable list = (IEnumerable)data;
foreach(var testModel in list)
{
// manipulate testModel into an object SSIS can digest
}
You could try using AutoMapper to map your TestModel classes to each other. It's easy to setup and use.
This is off the top of my head so it might need tweaking, but the code would look very similar to this:
var config = new MapperConfiguration(cfg => {
CreateMap<ST_a048e0de86e1432d8da6f60b5d7055db.TestModel
, SC_0573a66ec6c0486a98ee00cea4365654.TestModel>();
cfg.AddProfile<testModel>();
});
var mapper = config.CreateMapper();
Then you could convert the listTest using the mapper like this:
var convertedList = mapper.Map<ST_a048e0de86e1432d8da6f60b5d7055db.TestModel>
, IList<SC_0573a66ec6c0486a98ee00cea4365654.TestModel>>(listTest);
EDIT: Attempt 2
You said you tried moving the TestModel class to the second namespace. If not you may need to add it in or something equivalent. Then it might be as simple as trying this:
var listTest = data.Select(x => new TestModel
{ property1 = x.property1,
property2 = x.property2
//etc...
}).ToList();
That should give you a new List<TestModel>
I wanted to expand on John Ephraim Tugado's solution since it seemed a little incomplete
public void Main()
{
try
{
List<TestModel> testModelList = BuildObjectList();
TestModel testModel = new TestModel();
testModel.prop1 = "new prop";
testModel.prop2 = true;
testModelList.Add(testModel);
//Now that we have a testModelList in the correct local class we can
//modify it and load it back into the globally held variable for
//another Component to use
Dts.Variables["User::myObjectList"].Value = testModelList;
Dts.TaskResult = (int)ScriptResults.Success;
}
catch
{
Dts.TaskResult = (int)ScriptResults.Failure;
}
}
private List<TestModel> BuildObjectList()
{
try
{
List<TestModel> RunningList = new List<TestModel>();
TestModel localModel = new TestModel();
var data = Dts.Variables["User::myObjectList"].Value;
IEnumerable enumDataList = (IEnumerable)data;
foreach (var currentObj in enumDataList)
{
localModel = GetSingleResult(currentObj);
RunningList.Add(localModel);
localModel = new TestModel();
}
return RunningList;
}
catch
{
return new List<TestModel>();
}
}
private TestModel GetSingleResult(object currentObj)
{
try
{
TestModel returnedResult = new TestModel();
PropertyInfo[] properties = currentObj.GetType().GetProperties();
foreach (PropertyInfo pi in properties)
{
switch (pi.Name)
{
case "prop1":
returnedResult.prop1 = pi.GetValue(currentObj, null).ToString();
break;
case "prop2":
returnedResult.prop2 = Convert.ToBoolean(pi.GetValue(currentObj, null));
break;
default:
break;
}
}
return returnedResult;
}
catch {
return new TestModel();
}
}
internal class TestModel
{
internal string prop1 { get; set; }
internal bool prop2 { get; set; }
}
I have a three polymorphed classes. Based on user input the class should be set to that user's input. So the child class is decided by a user, and should make for 'class = new inputClass'. The snippet of code looks like:
public void characterGeneration(string classSelected)
{
foreach (string classInList in classes.ClassList)
{
if (classSelected == classInList)
{
PlayerOneStats = new Mage();
}
}
PlayerOneStats.generateStats();
}
Where it says PlayerOneStats = new Mage();, I want the Mage() to be the user input.
I've looked at Activator, Assembly, using Type, trying to cast over to the parent of GenerateStats, but nothing works. I've found many people saying it works, and one link that says it doesn't work. Can somebody please clear this up for me? Thank you very much!
Are you sure Activator doesn't work? Activator.CreateInstace("assembly-name", "type-name") seems like exactly what you want. What doesn't work?
What is the base class of Mage (and the other classes a user can select)? You should be able to do this:
public void characterGeneration(string classSelected)
{
foreach (string classInList in classes.ClassList)
{
if (classSelected == classInList)
{
PlayerOneStats = (GenerateStats)Assembly.GetExecutingAssembly().CreateInstance("YourNamespace." + classSelected);
break;
}
}
PlayerOneStats.generateStats();
}
Make sure that you include the namespace the type you want is contained in and this should work for you:
string classSelected = "testCode.Mage";
var player = Activator.CreateInstance(Type.GetType(classSelected));
Since Activator.CreateInstance() returns an object you will have to cast - in your case it would make sense to cast to an interface that all your player classes implement:
var player = (IPlayerCharacter) Activator.CreateInstance(Type.GetType(classSelected));