I am trying to read some .sql files that I have embedded in a resx file called SQLUpdateScripts. All my files in SQLupdateScripts.resx are in format 00001.sql,00002.sql,0003.sql etc.
I want to read the content of each sql file and execute it based on two variables
for (int i = DbVersion+1; i <= FileDbVersion; i++)
{
string updateScripts = "script_" + i.ToString("D5");
....
var sqlqueries = myString.Split(new[] { "\nGO", "\ngo" }, StringSplitOptions.RemoveEmptyEntries);
etc....
I tried many suggestions in stackoverflow but all of them are returning null or exception
ResourceManager sqLUpdateScripts = new ResourceManager(typeof(SQLUpdateScripts));
string myString = sqLUpdateScripts.GetString(updateScripts);
also
object[] args = new object[] { updateScripts };
SQLUpdateScripts obj = new SQLUpdateScripts();
obj.GetType().InvokeMember(updateScripts,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
Type.DefaultBinder, obj, args);
The code in SqlUpdateScripts.Designer.cs for every sql file is
internal static string script_00001
{
get {
return ResourceManager.GetString("script_00001", resourceCulture);
}
}
internal static string script_00002
{
get {
return ResourceManager.GetString("script_00002", resourceCulture);
}
}
I read that I must use reflection to call each file as method by using the invokeMember gives me Exception that cannot find class SqlUpdateScripts.script_0001'.
When I tried using the ResourceManager it just return myString as null.
If I called by hand:
var sqlqueries = SQLUpdateScript.script_0001.Split(new[] { "\nGO", "\ngo" }, StringSplitOptions.RemoveEmptyEntries);
it works fine but I want to use variables for the filename and not a standard filename
I found the problem.
It was a dump error I made. I gave wrong values while testing in DBVersion and fileDBVersion so when I used
for (int i = DbVersion+1; i <= FileDbVersion; i++)
instead of giving me value script_00001 it gave me the value script_01001
So now that it is working I found that you can call files as strings in resx files with two ways
First:
ResourceManager sqLUpdateScripts = new ResourceManager(typeof(SQLUpdateScripts));
string myString = sqLUpdateScripts.GetString(updateScripts);
Second way:
string resVal = SQLUpdateScripts.ResourceManager.GetString(updateScripts, Thread.CurrentThread.CurrentCulture);
GetString in second way has two overloads, one is with out CultureInfo and one is with specify the CultureInfo in case you want to call another language for example.
I don't know if there is a way to avoid future mistakes while calling wrong resx resource , maybe if there is a way to enumerate all the resources and then just choose one.
If I find a way I will add it here.
Related
I need to split it to another function and return see whether it contains some value or not but I'm not so sure how to make it. Example
exList = getList(ref Path, type);
if(exList.Count > 0){
Do something...
}
Im not so sure this part how to write... this is my half work
static object getList(ref string Path, string type)
{
exList = new List<Email>();
string[] jsonFileList = Directory.GetFiles(Path, type + "_*.json");
if (jsonFileList.Length > 0)
{
//read json file
foreach (string file in jsonFileList)
{
if (File.Exists(file))
{
exList.Add(JsonConvert.DeserializeObject<ExceptionEmail>(File.ReadAllText(file)));
File.Delete(file);
}
}
}
return something;
}
check below code
static List<Email> getList(ref string Path, string type)
{
exceptionList = new List<Email>();
string[] jsonFileList = Directory.GetFiles(Path, type + "_*.json");
if (jsonFileList.Length > 0)
{
//read json file
foreach (string file in jsonFileList)
{
if (File.Exists(file))
{
List.Add(JsonConvert.DeserializeObject<ExceptionEmail>(File.ReadAllText(file)));
File.Delete(file);
}
}
}
return exceptionList;
}
Your function needs to return a list and you need to save the return value into a variable.
// not List = ..., List is a class, you need a new instance of a list.
List<Email> list = getList(path, type);
if (list.Count > 0)
{
// Do Something
}
// [...]
private List<Email> getList(string path, string type)
{
List<Email> ret = new List<Email>();
string[] jsonFileList = Directory.GetFiles(path, type + "_*.json");
if (jsonFileList.Length > 0)
{
//read json file
foreach (string file in jsonFileList)
{
if (File.Exists(file))
{
// not List.Add(), List is a class, you need to add to the instance of a list.
ret.Add(JsonConvert.DeserializeObject<ExceptionEmail>(File.ReadAllText(file)));
// File.Delete(file); // The method shouldn't delete files when it's name is getList, delete them after handling in the calling method.
}
}
}
return ret;
}
Also you should work on your style.
Please do use strong types wherever possible. (i.E. no Object)
Try to avoid static functions and variables unless you need them.
For readability write the access modifier.
Variable and argument names should be lower case and constants capitals. Only classes, enums and interfaces, structs, etc. should start with a capital letter.
Only use reference arguments if you need to. Call by value is default for a reason. (The reason being encapsulation, and avoiding side effects. Also you would expect the function here to alter path if you say it's byref, which it doesn't.)
getList shouldn't delete files. You wouldn't expect that from a name like that. Delete the files AFTER you processed them in the loop in the calling method.
please review the difference between classes and objects/instances.
please review function calls and return values.
Could you help me to solve some problem, please?
I want to get one of the ImageFormat properties (like ImageFormat.Png or ImageFormat.Jpeg, etc). And I need it dynamically.
The method should look like (as I see it):
private List<ImageFormat> GetValidImageFormats()
{
List<ImageFormat> result = new List<ImageFormat>()
foreach (string extension in ValidExtensions)
{
// do some expression magic
}
}
I have problem with with code in foreach. I dont even sure of using Expression Trees.
I need it for my custom validator for uploaded image files. Any help is just great. +1 for any relevant solution.
EDIT:
Possible values of ValidExtensions = new[] {"jpg", "png", "jpeg", "bmp", "gif", "icon"}
As long as your list of extensions matches what's returned from the ImageFormat class, like this:
private List<string> ValidExtensions = new List<string> {"bmp", "jpeg", "png"};
You can convert each of those strings to their equivalent ImageFormat with reflection:
private List<ImageFormat> GetValidImageFormats()
{
var t = typeof(ImageFormat);
return ValidExtensions.Select(x =>
(ImageFormat)t.GetProperty(x.Substring(0, 1).ToUpper() + x.Substring(1))
.GetValue(null)).ToList();
}
svick left an alternate solution in the comments, and it more clearly indicates your intention.
Instead of converting the string to title case to make it match the method call, you can use a different overload of GetProperty() to pass a bitmask telling it how to search... in this case, find a public static member and ignore the case altogether.
private List<ImageFormat> GetValidImageFormats()
{
var t = typeof(ImageFormat);
return ValidExtensions.Select(x =>
(ImageFormat)t.GetProperty(x, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Static)
.GetValue(null)).ToList();
}
I am implementing a DynamicObject. In the TryInvokeMethod, in addition to the arguments ( passed in to the method), I also need the names for the parameters if they have been used.
I can see that binder.CallInfo.ArgumentNames does indeed provides the names, but I am not able to associate them with the values. Is there any way to do so, or am I hoping against hope:
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
var names = binder.CallInfo.ArgumentNames;
foreach (var arg in args)
{
arguments.Add(new Argument(arg));
}
//I need to associate the names and the args
result = this;
return true;
}
So for example if I do a call like:
myDynamicObject.DynamicInvoke("test", something: "test2")
I have test and test2, and also something, but I am not able to get the information that something was the name for test2 and test had no name.
I had to use the fact that named arguments occur only after the non-named ones are specified ( thanks to user629926 for pointing to the obvious) and initial code:
var names = binder.CallInfo.ArgumentNames;
var numberOfArguments = binder.CallInfo.ArgumentCount;
var numberOfNames = names.Count;
var allNames = Enumerable.Repeat<string>(null, numberOfArguments - numberOfNames).Concat(names);
arguments.AddRange(allNames.Zip(args, (flag, value) => new Argument(flag, value)));
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..
I'm writing an application that runs "things" to a schedule.
Idea being that the database contains assembly, method information and also the parameter values. The timer will come along, reflect the method to be run, add the parameters and then execute the method.
Everything is fine except for the parameters.
So, lets say the method accepts an ENUM of CustomerType where CustomerType has two values of CustomerType.Master and CustomerType.Associate.
EDIT
I don't know the type of parameter that will be getting passed in. ENUM used as an example
END OF EDIT
We want to run Method "X" and pass in parameter "CustomerType.Master". In the database, there will be a varchar entry of "CustomerType.Master".
How do I convert the string "CustomerType.Master" into a type of CustomerType with a value of "Master" generically?
Thanks in advance,
Jim
OK, the scope of the question shifted but my original observation and objection to some other solutions still stands.
I think you don't/can't want to use 'generics' here. You don't know the type ahead of time, and since you will need to create the type, there is no need to use a generic implementation because MethodBase.Invoke takes an array of Object.
This code assumes you are instantiating the target from database field. If not just adjust accordingly.
Of course this is not all encompassing and has no useful exception handling, but it will allow you to dynamically execute arbitrary methods on an arbitrary type with arbitrary parameters values all coming from string values in a row.
NOTE: there are many many many scenarios in which this simple executor will not work. You will need to ensure that you engineer your dynamic methods to cooperate with whatever strategy you do end up deciding to use.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Reflection;
using NUnit.Framework;
namespace DynamicMethodInvocation
{
[TestFixture]
public class Tests
{
[Test]
public void Test()
{
// from your database
string assemblyQualifiedTypeName = "DynamicMethodInvocation.TestType, DynamicMethodInvocation";
string methodName = "DoSomething";
// this is how you would get the strings to put in your database
string enumString = Executor.ConvertToString(typeof(AttributeTargets), AttributeTargets.Assembly);
string colorString = Executor.ConvertToString(typeof(Color), Color.Red);
string stringString = "Hmm... String?";
object result = Executor.ExecuteMethod(assemblyQualifiedTypeName, methodName,
new[] { enumString, colorString, stringString });
Assert.IsInstanceOf<bool>(result);
Assert.IsTrue((bool)result);
}
}
public class TestType
{
public bool DoSomething(AttributeTargets #enum, Color color, string #string)
{
return true;
}
}
public class Executor
{
public static object ExecuteMethod(string assemblyQualifiedTypeName, string methodName,
string[] parameterValueStrings)
{
Type targetType = Type.GetType(assemblyQualifiedTypeName);
MethodBase method = targetType.GetMethod(methodName);
ParameterInfo[] pInfo = method.GetParameters();
var parameterValues = new object[parameterValueStrings.Length];
for (int i = 0; i < pInfo.Length; i++)
{
parameterValues[i] = ConvertFromString(pInfo[i].ParameterType, parameterValueStrings[i]);
}
// assumes you are instantiating the target from db and that it has a parameterless constructor
// otherwise, if the target is already known to you and instantiated, just use it...
return method.Invoke(Activator.CreateInstance(targetType), parameterValues);
}
public static string ConvertToString(Type type, object val)
{
if (val is string)
{
return (string) val;
}
TypeConverter tc = TypeDescriptor.GetConverter(type);
if (tc == null)
{
throw new Exception(type.Name + " is not convertable to string");
}
return tc.ConvertToString(null, CultureInfo.InvariantCulture, val);
}
public static object ConvertFromString(Type type, string val)
{
TypeConverter tc = TypeDescriptor.GetConverter(type);
if (tc == null)
{
throw new Exception(type.Name + " is not convertable.");
}
if (!tc.IsValid(val))
{
throw new Exception(type.Name + " is not convertable from " + val);
}
return tc.ConvertFrom(null, CultureInfo.InvariantCulture, val);
}
}
}
I would think you have 2 major options:
Store the type name along with the parameter value and use that to cast things using Type.GetType(string) to resolve the type in question.
Standardize all the methods to be called this way to accept an array of strings, and expect the methods to do any necessary casting.
I know you've stated that you're not doing option 1, but it would help things from the standpoint of calling the functions.
Option 2 is the far more 'generic' way to handle the situation, assuming all values can be represented by and cast/converted from strings to the appropriate type. Of course, that only helps if you actually have control over the definition of the methods being called.
Below is a useful extension method I use in .NET 3.5.
With this extension method available, your code could look like this:
var valueInDb = GetStringFromDb().Replace("CustomerType.", string.Empty);
var value = valueInDb.ToEnum(CustomerType.Associate);
By supplying the default value in the parameter, the compiler will know which Enum you want your string to be turned into. It will try to find your text in the Enum. If it doesn't it will return the default value.
Here is the extension method: (this version also does partial matches, so even "M" would work nicely!)
public static T ToEnum<T>(this string input, T defaultValue)
{
var enumType = typeof (T);
if (!enumType.IsEnum)
{
throw new ArgumentException(enumType + " is not an enumeration.");
}
// abort if no value given
if (string.IsNullOrEmpty(input))
{
return defaultValue;
}
// see if the text is valid for this enumeration (case sensitive)
var names = Enum.GetNames(enumType);
if (Array.IndexOf(names, input) != -1)
{
// case insensitive...
return (T) Enum.Parse(enumType, input, true);
}
// do partial matching...
var match = names.Where(name => name.StartsWith(input, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
if(match != null)
{
return (T) Enum.Parse(enumType, match);
}
// didn't find one
return defaultValue;
}
I still don't fully understand your question... however, you say "Everything is fine except for the parameters."
I'll assume "CustomerType" the name of a property on your object, and "Master" is the string value you want to put in that property.
Here is (another) extension method that may help.
Once you have your new object and the value and property name from the database field, you could use this:
// string newValue = "Master";
// string propertyName = "CustomerType";
myNewObject.SetPropertyValue(propertyName, newValue)
Method:
/// <summary>Set the value of this property, as an object.</summary>
public static void SetPropertyValue(this object obj,
string propertyName,
object objValue)
{
const BindingFlags attr = BindingFlags.Public | BindingFlags.Instance;
var type = obj.GetType();
var property = type.GetProperty(propertyName, attr);
if(property == null) return;
var propertyType = property.PropertyType;
if (propertyType.IsValueType && objValue == null)
{
// This works for most value types, but not custom ones
objValue = 0;
}
// need to change some types... e.g. value may come in as a string...
var realValue = Convert.ChangeType(objValue, propertyType);
property.SetValue(obj, realValue, null);
}
If you are using .NET 4 you can do the following.
var result = default(CustomerType);
if (!Enum.TryParse("Master", out result))
{
// handle error
}