C# Generic parameter from a string variable - c#

I have two classes, Customer and Country. Customer has a property called HomeCountry, which i have decorated with a custom attribute called "Lookup" and takes a string parameter "Country". The purpose is, when I am using the Customer class, the item in HomeCountry must exist in the Country class (which happens to be a list).
I am using reflection to iterate the Customer class, it finds the attribute and i want it to check the value in the list of country items. So far I have:
foreach (PropertyInfo _pi in object.GetType().GetProperties()) {
IEnumerable<Attribute> _attrs = _pi.GetCustomAttributes();
foreach (Attribute _a in _attrs) {
Object obj = Activator.CreateInstance(_type, null);
// what goes here?
}
}
I have a method:
public T Populate<T>(params string[] _parameters)
I think i want to do
List<obj> v = populate<obj>();
or
List<typeof(obj)> v = populate<typeof(obj)>();
but obviously nothing works! Can anybody help me ?
Thanks

OK i will try and provide a full example:
I have a CUSTOMER_ORDER class:
public class CUSTOMER_ORDER {
public CUSTOMER_ORDER() {}
[Key(0)]
public string OrderNumber {get;set;}
public MPCCOM_SHIP_VIA ShipVia {get;set;}
}
Then the MPCCOM_SHIP_VIA class:
public class MPCCOM_SHIP_VIA {
public MPCCOM_SHIP_VIA() {}
[Key(0)]
public string ID {get;set;}
public string Description {get;set;}
}
I have a method called Populate< T > which takes a class and then uses reflection to loop all the properties and build a select statement, executes it, and then returns the data and populates the object:
public T Populate<T>(params string[] #Parameters)
{
Type _t = typeof(T);
dynamic _o = Activator.CreateInstance(typeof(T), null);
SqlBuilder _sb = new SqlBuilder();
_sb.Table = string.Format("{0}.{1}", _Owner, _t.Name.ToString());
foreach (PropertyInfo p in _t.GetProperties(Utilities.BindingFlags))
{
if (p.GetMethod.IsPrivate == false) _sb.Fields.Add(p.Name.ToString());
IEnumerable<Attribute> _attrs = p.GetCustomAttributes();
foreach (Attribute _a in _attrs)
{
if (_a.TypeId.ToString().Equals(typeof(Key).FullName))
{
int _position = ((Key)_a).Position;
try
{
string _parameter = #Parameters[_position];
_sb.Where.Add(string.Format("{0} = '{1}'", p.Name, _parameter));
}
catch {}
}
}
}
using (OleDbCommand _cmd = new OleDbCommand())
{
_cmd.Connection = this._cn;
_cmd.CommandText = _sb.SQL;
if (_trn != null) _cmd.Transaction = _trn;
_cmd.CommandType = System.Data.CommandType.Text;
using (OleDbDataReader _reader = _cmd.ExecuteReader())
{
if (_reader.Read())
{
for (int x = 0; x < _reader.FieldCount; x++)
{
foreach (PropertyInfo p in _t.GetProperties(Utilities.BindingFlags))
{
if (p.GetMethod.IsPrivate == false)
{
if (p.Name.Equals(_reader.GetName(x).ToString()))
{
dynamic _val = _reader.GetValue(x);
if (p.ReflectedType.BaseType.Name.Equals(""))
{
// what goes here!
}
try
{
p.GetSetMethod(true).Invoke(_o, new object[] { _val });
}
catch { }
break;
}
}
}
}
}
else
{
throw new DatabaseObjectNotFound(_t.Name.ToString(), string.Join(",",#Parameters));
}
}
}
return (T)_o;
}
So, as i read an order, the source DB gets the key to the MPCCOM_SHIP_VIA in the respective field, i want to call the same Populate method against the MPCCOM_SHIP_VIA object with the key. I hope this makes more sense demonstrating what i want to do. And thanks

After some hunting around, this is the answer i was looking for...
MethodInfo method = typeof(class).GetMethod("Populate");
method = method.MakeGenericMethod(p.PropertyType);
_val = method.Invoke(class, new object[] { _prms });
I guess my issue was i was asking the wrong question!

Related

How can I change a custom attribute property on a generic type <T> method input parameter

I have a generic method that takes T, GetResult<T>()
T represents many objects which have custom attribute [JsonProperty(Required = Required.Always)] on most of their properties. Within GetResult<T>(), I need to change T's properties to [JsonProperty(Required = Required.Default)] and I need the next method which is called from within GetResult to get the modified version of T.
Is that possible? If so, what adjustments would I need to my sample program below?
namespace StackOverflow1
{
using System;
using Newtonsoft.Json;
class Program
{
static void Main(string[] args)
{
GetResult<Person>();
Console.Read();
}
private static T GetResult<T>() where T : Base
{
// Entering this method, T is passed with Name property set to [Required = Required.Always]
// I'm changing T's Name property to [Required = Required.Default]
var properties = typeof(T).GetProperties();
foreach (var property in properties)
{
var customAttributes = property.GetCustomAttributes(true);
foreach (var attribute in customAttributes)
{
if (attribute.GetType().Name == nameof(JsonPropertyAttribute))
{
Console.WriteLine($"Original: {((JsonPropertyAttribute)attribute).Required}");
// this is the change! But it looks like this does not actually change T which I need to forward into BuildSingleResult<T>
((JsonPropertyAttribute)attribute).Required = Required.Default;
Console.WriteLine($"After modification: {((JsonPropertyAttribute)attribute).Required}");
}
}
}
// I need T to be the **modified version** which would have [Required = Required.Default]
return BuildSingleResult<T>();
}
private static T BuildSingleResult<T>() where T : Base
{
// **** this is just to write out JsonPropertyAttribute ***
var props = typeof(T).GetProperties();
foreach (var p in props)
{
var customAttributes = p.GetCustomAttributes(true);
foreach (var attr in customAttributes)
{
if (attr.GetType().Name == nameof(JsonPropertyAttribute))
{
// This shows that T still has [Required = Required.Always] but I need it to have [Required = Required.Default]
Console.WriteLine($"(expecting Default): {((JsonPropertyAttribute)attr).Required}");
}
}
}
// *** end of debug ***
return new Person { Name = "X" } as T;
}
}
class Base
{
[JsonProperty(Required = Required.Always)]
public string Name { get; set; }
}
// This is just to demonstrate using a generic type!
class Person : Base { }
}

Converting data from one class to another

I'm trying to convert data from one ExtensionMethods class to another class called ComboBoxViewItem
ExtensionMethods:
public static class ExtensionMethods
{
public static int GetDisplayItemId(this ComboBox combobox)
{
if (combobox.SelectedItem == null)
return 0;
else
return ((DisplayItems)combobox.SelectedItem).Id; //Error here
}
}
ComboBoxViewItem
class ComboBoxViewItem<T>
{
private string name;
public T Item { get; set; }
public ComboBoxViewItem(T item, string name)
{
this.Item = item;
this.name = name;
}
public ComboBoxViewItem(T item)
{
var prop = item.GetType().GetProperty("Name");
if (prop == null)
throw new ArgumentException("This object does not have a Name property, please use the other contructor.");
if (prop.PropertyType != typeof(string))
throw new ArgumentException("The property Name MUST be of type string. Please use the other contructor instead.");
this.Item = item;
this.name = (string)prop.GetValue(item);
}
public override string ToString()
{
return name;
}
}
Step 1 Now what I'm trying to do is load data from my WCF service into one of my comboboxes like this:
public async Task LoadCompanies()
{
using (MKCServiceClient service = new MKCServiceClient())
{
var companies = await service.GetCompaniesAsync();
foreach (var company in companies)
cmbQuoteCompany.Items.Add(new ComboBoxViewItem<Company>(company));
cmbQuoteCompany.SelectedIndex = 0;
}
} //Coding works fine and loads data into the combobox
Step 2 I want to add that data that was selected in the combobox to be added else where in another table using this method below:
private async void btnQuoteAdd_Click(object sender, RoutedEventArgs e)
{
using (MKCServiceClient service = new MKCServiceClient())
{
quoteInformation = await service.GetQuoteAsync(new QuoteData
{
CompanyId = cmbQuoteCompany.GetDisplayItemId(), //I use ExtensionMethods class here
BranchId = cmbQuoteBranch.GetDisplayItemId(), //Here
CustomerId = cmbQuoteContact.GetDisplayItemId(), //Here
CustomerRFQ = txtQuoteCustomerRFQ.Text,
Date = dpQuoteDate.Text,
Item = txtQuoteItem.Text,
Material = txtQuoteMaterial.Text,
Description = txtQuoteDescription.Text,
Quantity = Convert.ToInt32(txtQuoteQTY.Text)
});
await service.FinalizeQuoteAsync(finalizeQuote);
}
}
My end goal is to get the Id of the selected item in my combobox and then insert it into my database.
After I call the btnQuoteAdd_Click method, my application crashes and gives me the following error
Unable to cast object of type
'MKCWorkflowApplication.ComboBoxViewItem`1[MKCWorkflowApplication.WorkflowService.Company]'
to type 'MKCWorkflowApplication.DisplayItems'.
The reason why i'm posting this issue here is because i've been given this coding from a friend who knows a lot more that C# than me and we are no longer in contact, so I don't know how to get past this issue :(
So if anyone could figure out what is going on, please help! Thank you.
Your extension method GetDisplayItemID expects a combobox filled with instances of the classDisplayItem not filled with instances of a ComboBoxViewItem and thus the internal cast fails. A possible workaround could be written if your classes Company, Quote and Customer provide an indentical named property ID.
You could rewrite your ComboBoxViewItem<T> class in this way
class ComboBoxViewItem<T>
{
public string Name;
public int ID;
public T Item { get; set; }
public ComboBoxViewItem(T item, string name, int id)
{
this.Item = item;
this.Name = name;
this.ID = id;
}
public ComboBoxViewItem(T item)
{
var prop = item.GetType().GetProperty("Name");
if (prop == null)
throw new ArgumentException("This object does not have ...");
if (prop.PropertyType != typeof(string))
throw new ArgumentException("The property Name MUST be of type...");
this.Name = (string)prop.GetValue(item);
prop = item.GetType().GetProperty("ID");
if (prop == null)
throw new ArgumentException("This object does not have ...");
if (prop.PropertyType != typeof(int))
throw new ArgumentException("The property ID MUST be of ...");
this.ID = (int)prop.GetValue(item);
this.Item = item;
}
public override string ToString()
{
// C# 6.0 string interpolation
//return string.Format($"{ID}, ({Name})");
// C# Standard string formatting
return string.Format("{0}, ({1})", ID, Name);
}
}
Now you could retrieve the ID from your comboboxes using this syntax without using the old extension method or defining a new one.
CustomerId = (cmbQuoteContact.SelectedItem as ComboBoxViewItem<Customer>).Item.ID
....

logging all properties of an object in c#. how to log inner object properties as well?

i am trying to (1) log all properties of an object, and (2) all properties of a specific object type within. i can do the (1) but not (2).
this is the case now.
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(object1))
{
string name = descriptor.Name;
object value = descriptor.GetValue(object1);
logger.Debug(String.Format("{0} = {1}", name, value));
}
what i need is something like:
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(object1))
{
string name = descriptor.Name;
object value = descriptor.GetValue(object1);
logger.Debug(String.Format("{0} = {1}", name, value));
// TODO check if the current property of object1 is of type object2, how?
if (...) {
// TODO repeat the process for object2
foreach (PropertyDescriptor innerdescriptor in TypeDescriptor.GetProperties(object2))
{
string innername = innerdescriptor.Name;
object innervalue = innerdescriptor.GetValue(object2);
logger.Debug(String.Format(" {0} = {1}", innername, innervalue));
}
} // end if
}
however, this second thing doesn't work no matter what i try. so, please help.
update
i have an answer (by #Alex Art.) to the check
if (descriptor.PropertyType == typeof(the type that you expecting) ) { ... }
now the only thing that remains is the inner object properties logger!
I think it can be achieved by using reflection (But you should be aware of performance penalty):
public void LogProps(Object object1)
{
var objType = object1.GetType();
IList<PropertyInfo> properties = new List<PropertyInfo>(objType.GetProperties());
foreach (PropertyInfo prop in properties)
{
var propValue = prop.GetValue(object1, null);
if(prop.PropertyType == typeof(yourTypeHere))
{
LogProps(propValue);
}
else
{
logger.Debug(String.Format("{0} = {1}", prop.Name, propValue));
}
}
}
I also used a recursion here which is also could be problematic if you have some long hierarchy
Regarding your solution:
// TODO check if the current property of object1 is of type object2,
how?
Did you try using PropertyDescriptor.PropertyType?:
object value = descriptor.GetValue(object1);
if (descriptor.PropertyType == typeof(the type that you expecting) )
{
foreach (PropertyDescriptor innerdescriptor in TypeDescriptor.GetProperties(value)
{
string innername = innerdescriptor.Name;
object innervalue = innerdescriptor.GetValue(object2);
logger.Debug(String.Format(" {0} = {1}", innername, innervalue));
}
} // end if
If this is for the purpose of logging the state of the object at runtime, you'll probably find it easier to serialize the whole object as JSON and store that instead. This has the added benefit of being human readable and more flexible than a custom solution.
To ignore certain properties, apply the IgnoreDataMemberAttribute to them.
i.e.
[DataContract]
public class MyObjectParent
{
[DataMember]
public int ImportantValue { get; set; }
[DataMember]
public MyObjectChild Child { get; set; }
[IgnoreDataMember]
public AnotherClassThatIWantToIgnore IgnoreMe { get; set; }
}
[DataContract]
public class MyObjectChild
{
[DataMember]
public string ImportantValue { get; set; }
}
public string ObjectAsJson<T>(T obj) where T : class
{
var serializer = new DataContractJsonSerializer(typeof(T));
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, obj);
return Encoding.Default.GetString(stream.ToArray());
}
}

Is there a var.contains - C#

I have a question about the following code:
private void Filter (object sender, Android.Text.TextChangedEventArgs e)
{
List<Animal> animalList = new List<Animal>();
if(!string.IsNullOrEmpty(_editText.Text))
{
foreach (string str in _animalList)
{
if (str.Contains(_editText.Text))
{
animalList.Add (str);
}
}
}
_listView.Adapter = new AnimalAdapter(this, _animalList = animalList);
}
The Animal class:
public class Animal
{
private readonly int _intKey;
public int AnimalNumber { get; private set; }
public int StableNumber { get; private set; }
public int LactoseNumber { get; private set; }
public Animal ( int intKey, int animalNumber, int stableNumber, int lactoseNumber )
{
_intKey = intKey;
AnimalNumber = animalNumber;
StableNumber = stableNumber;
LactoseNumber = lactoseNumber;
}
public override string ToString ()
{
return "Number: " + AnimalNumber + "\nGroup: " + StableNumber + "\nLactation: " + LactoseNumber;
}
}
Declaration of _animalList:
private List<Animal> _animalList;
i need to check if the _animalList Contains the input of the _editText.Text.
But _animalList isn't a string so i need to use a var.
Is there something like a var.Contains or do i have to use something else?
Contains method is available for string type. You will need to cast your object to string.
A/c to your class definition you should do like:
foreach (Animal str in _animalList)
{
if (str.ToString().Contains(_editText.Text)) //using user defined "ToString()"
{
animalList.Add (str);
}
}
You can also check individual properties:
foreach (Animal str in _animalList)
{
if (str.AnimalNumber.ToString().Contains(_editText.Text)) //if "AnimalNumber" is like "_editText.Text"
{
animalList.Add (str);
}
}
Instead of trying to filter using ToString, it would be better to use the real property values. For example:
var number = Convert.ToInt32(_editText.Text);
var filteredList = _animalList
.Where(x => x.AnimalNumber == number ||
x.StableNumber == number ||
x.LactoseNumber == number)
.ToList();
Otherwise, user could type "Number" and since your ToString override contains that string, all of the items in the list would match positively.
(I didn't include any validation or error checking in the code above, so you should consider those as well).
var inputText = _editText.Text;
int enteredNumber;
// you should make sure that the inputText is always an int
var isInt = int.TryParse(inputText, out enteredNumber);
//for example, if you are going to find by AnimalNumber, which is an int, you can use this. .
if (isInt){
foreach (var animal in _animalList){
var animalNumber = animal.AnimalNumber;
if (animalNumber == enteredNumber)
{
animalList.Add(animal);
}
}
}
Edit (LINQ alternative):
if (isInt){
animalList.AddRange(from animal in _animalList
let animalNumber = animal.AnimalNumber
where animalNumber == enteredNumber
select animal);
}
_animalList.Select(a => a.ToString()).Contains(_editText.Text)
This expression returns true if the output of the ToString method of any animal object equals _editText.Text.
_animalList.Select(a => a.ToString()).Any(str => str.Contains(_editText.Text))
This expression returns true if the output of the ToString method of any animal object contains _editText.Text (as a substring). This is equivalent to Shaharyar's answer.
var animalList = _animalList.Where(a => a.ToString().Equals(_editText.Text)).ToList();
var animalList = _animalList.Where(a => a.ToString().Contains(_editText.Text)).ToList();
These statements filter the input list directly.

Find all classes which derive from a specific base class and add them to the registry

I have a base class called BaseStatus which looks like this:
public class BaseStatus
{
public int UnitId { get; protected set; }
public UInt16 StatusValue { get; protected set; }
public string StatusCode { get; protected set; }
public string StatusDescription { get; protected set; }
public BaseStatus()
{
this.UnitId = -1;
this.StatusValue = 0;
this.StatusCode = null;
this.StatusDescription = null;
}
}
Furthermore i have two or more other base classes which derive from BaseStatus and define a other unit id. For example the two classes
public class BaseGlobalStatus : BaseStatus
{
public BaseGlobalStatus()
{
base.UnitId = -1;
}
}
public class BaseGcmGdmStatus : BaseStatus
{
public BaseGcmGdmStatus()
{
base.UnitId = 2;
}
}
public class BaseCcuStatus : BaseStatus
{
public BaseCcuStatus()
{
base.UnitId = 1;
}
}
The Background is that i want to derive from for example BaseCcuStatus and have the correct UnitId in the derived class.
Now i define my correct status classes for example:
public class StatStErrDefinition : BaseGlobalStatus
{
public StatStErrDefinition()
: base()
{
base.StatusDescription = "Kommando nicht zulässig, unit im state ERROR";
base.StatusCode = "STAT_ST_ERR";
base.StatusValue = 3;
}
}
public class GcmStErrDefinition : BaseGcmGdmStatus
{
public GcmStErrDefinition()
: base()
{
base.StatusDescription = "Kommando nicht zulässig, unit im state ERROR";
base.StatusCode = "STAT_ST_ERR";
base.StatusValue = 3;
}
}
public class CcuStErrDefinition : BaseCcuStatus
{
public CcuStErrDefinition()
: base()
{
base.StatusDescription = "Kommando nicht zulässig, unit im state ERROR";
base.StatusCode = "STAT_ST_ERR";
base.StatusValue = 3;
}
}
For my understading, the three classes StatStErrDefinition, GcmStErrDefinition and CcuStErrDefinition should have the UnitId which is set in the derived BaseClass?
Now that i have defined my three Status Classes i want to get them into a registry. Currently im using this piece of code to try get them. Problem is that the result has no items.
registry = new StatusDictionary<UInt16, BaseStatus>();
var unitStatus = typeof(BaseStatus)
.Assembly.GetTypes()
.Where(x => x.BaseType == typeof(BaseStatus))
.Select(x => new
{
StatusType = x,
UnitId = x.GetProperty("UnitId", BindingFlags.Public)
StatVal = x.GetProperty("StatusValue", BindingFlags.Public)
}
)
.Where(x => x.StatVal != null && x.UnitId != null)
.Select(x => new
{
UnitId = (int)x.UnitId.GetValue(null, null),
StatusValue = (UInt16)x.StatVal.GetValue(null, null),
Factory = (Func<BaseStatus>)(() => ((BaseStatus)Activator.CreateInstance(x.StatusType)))
});
try
{
foreach (var status in unitStatus)
{
if (status.UnitId == unitId
|| status.UnitId < 0)
registry.Register(status.StatusValue, status.Factory);
}
}
catch (Exception ex)
{
string temp = ex.Message;
}
After the LINQ expression the var unitStatus is empty...
Later, the registry call looks like that to get the specific class but that is unimportant at this point:
stat = StatusContainer.GetRegistry(this.unitTypeId).GetInstance(this.StatusValue);
For information:
I want to get the status class which should be in the registry by the unittypeid and the specific status value.
Currently my registry method does not work because he is not able to find any class. So there has to be a mistake somewhere. Thanks in advance
#Update 1
I changed my functionality a little bit:
registry = new StatusDictionary<UInt16, BaseStatus>();
//get all types of cucrent assembly
var allAssemblyTypes = Assembly.GetCallingAssembly().GetTypes();
//get all types from base status
var baseStatusTypes = allAssemblyTypes.Where(x => x.BaseType == typeof(BaseStatus));
//Place all concrete types in the foundtypes
List<Type> foundTypes = new List<Type>();
foreach (Type item in baseStatusTypes)
{
var temp = allAssemblyTypes.Where(x => x.BaseType == item)
.Select(x => new
{
StatusType = x,
UnitId = x.GetProperty("UnitId", BindingFlags.Public),
StatVal = x.GetProperty("StatusValue", BindingFlags.Public),
}
);
}
Temp contains now the correct type.
Problem is that if temp is type of StatStErrDefinition the StatusValue and UnitId Property is null.
The fact is that these members are instance members. Is there a way to get the values out of them?
First thing first : your LINQ query is pretty long.
divide it in different step and store them in different variables (or make properties out of them, whatever you prefer)
This is
easy to read / maintain
easy to debug
With this given I think you are able to solve your problem :)
To check if the class is of a certain type you could use the method .OfType
Use this method to get the value. Notice that you must make an instance in your case because the value change in your constructor.
public static object GetPropValue(Type src, string propName)
{
var prop = src.GetProperty(propName);
var instance = Activator.CreateInstance(src);
var value = prop.GetValue(instance);
return value;
}
Instead of
UnitId = x.GetProperty("UnitId", BindingFlags.Public),
use
UnitId = GetPropValue(x,"UnitId"),

Categories