I am trying to Split a search string containing Id=23||Header=This is a header||Description=This is a description into two arrays which I can use in the following context c.item[i] = property[i]. I attempted the solution below, but it does not match on the types any help would be appreciated :)
string[] stringSeparators = new string[] {"||"};
string[] testvalues = selectedSavedSearch.SearchString.Split(stringSeparators, StringSplitOptions.None).Select(sValue => sValue.Trim()).ToArray();
string[] items = new string[testvalues.Count()] ;
string[] properties = new string[testvalues.Count()] ;
for (int i = 0; i < testvalues.Count(); i++)
{
string[] values;
values = testvalues[i].Split('=').Select(sValue => sValue.Trim()).ToArray();
if (values.Count() > 0)
{
items[i] = values[0];
}
if (values.Count() > 1)
{
properties[i] = values[1];
}
}
for (int i = 0; i < items.Count(); i++)
{
currentSearch = typeof(BugManagerQueryOptions).GetProperty(items[i].ToString()).GetValue(properties[i], null);
}
I think you're misusing the GetValue:
currentSearch = typeof(BugManagerQueryOptions).GetProperty(items[i].ToString()).GetValue(properties[i], null);
Here's one way to get the value of the property via reflection:
PropertyInfo property = typeof(BugManagerQueryOptions).GetProperty(items[i]);
// query is the instance of BugManagerQueryOptions that you're trying to get the value from
var value= property.GetValue(query, null);
You're passing the property value to property.GetValue which will likely throw an exception.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnitTest
{
public class Program
{
public static void Main(string[] args)
{
BugManagerQueryOptions bmqo = new BugManagerQueryOptions
{
Id = 64,
Header = "This is a header, that has not been set by reflection (yet)",
Description = "This is a description that has not been set by reflection (yet)"
};
var pairs = "Id=23||Header=This is a header||Description=This is a description"
.Split(new string[] { "||" }, StringSplitOptions.RemoveEmptyEntries)
.Select(q => new KeyValuePair<string, string>(q.Split('=')[0], q.Split('=')[1]));
// TEST : Getting values from the BugManagerQueryOptions instance (bmqo)
foreach (KeyValuePair<string, string> pair in pairs)
{
Console.WriteLine(typeof(BugManagerQueryOptions).GetProperty(pair.Key).GetValue(bmqo, null));
}
// TEST : Setting values to the BugManagerQueryOptions instance (bmqo)
foreach (KeyValuePair<string, string> pair in pairs)
{
if (typeof(BugManagerQueryOptions).GetProperty(pair.Key).PropertyType == typeof(Int32))
{
typeof(BugManagerQueryOptions).GetProperty(pair.Key).SetValue(bmqo, Int32.Parse(pair.Value), null);
}
else
{
typeof(BugManagerQueryOptions).GetProperty(pair.Key).SetValue(bmqo, pair.Value, null);
}
}
// TEST: Getting values from the BugManagerQueryOptions instance (bmqo) AFTER being set by reflection
foreach (KeyValuePair<string, dynamic> pair in pairs)
{
Console.WriteLine(typeof(BugManagerQueryOptions).GetProperty(pair.Key).GetValue(bmqo, null));
}
Console.Read();
}
}
public class BugManagerQueryOptions
{
public Int32 Id { get; set; }
public String Header { get; set; }
public String Description { get; set; }
}
}
Related
passing a string like this to the code behind:
0,-1|1,-1|2,-1|3,-1|4,-1|5,-1|6,-1|7,-1|8,-1
I need to be able to asign the values before and after the "," symbol per each "|" symbol that exits in the string, into separated variables, "line" for first value (before the ",") and "group" for the next one (after the ",").
Right now I'm trying with this, but I'm having some issues with converting from string[] to string.
public static string GuardarGrupos(string parametro){
var data = parametro.Split(Convert.ToChar("|"));
var datos = "";
string[] linea;
var grupo = "";
//Iterate through each of the letters
foreach (var check in data)
{
datos = data[0];
linea = data[0].Split(Convert.ToChar(","));
}
return linea;
}
Any Idea how can I achieve this?
Make a class or struct to hold your values:
public class DataObject
{
public string X {get; set;}
public string Y {get; set;}
}
Return a List<T> of type DataObject
public static List<DataObject> GuardarGrupos(string parametro){
//List to return
List<DataObject> returnList = new List<DataObject>();
//Split the string on pipe to get each set of values
var data = parametro.Split('|'); //No need to do a convert.char(),
//String.Split has an overload that takes a character, use single quotes for character
//Iterate through each of the letters
foreach (var check in data)
{
//Split on comma to get the individual values
string[] values = check.Split(',');
DataObject do = new DataObject()
{
X = values[0];
Y = values[1];
};
returnList.Add(do);
}
return returnList;
}
Once you have the List<DataObject>, you can form line and group using linq and string.Join:
List<DataObject> myList = GuardarGrupos("0,-1|1,-1|2,-1|3,-1|4,-1|5,-1|6,-1|7,-1|8,-1");
string line = string.Join(",", myList.Select(x => x.X);
string group = string.Join(",", myList.Select(y => y.Y);
Instead of using local variables , create a Class that stores your retrieved values. then in the main you could handle/manipulate those values as required.
public class MyData
{
public string Datos { get; set; }
public string Linea { get; set; }
public string Grupo { get; set; }
}
public static List<MyData> myFunction(string parametro)
{
List<MyData> result = new List<MyData>();
var data = parametro.Split(Convert.ToChar("|"));
foreach (var check in data)
{
MyData temp = new MyData();
var line = check.Split(',');
temp.Datos = line[0];
temp.Linea = line[1];
temp.Grupo = check;
result.Add(temp);
}
return result;
}
static void Main(string[] args)
{
var t = myFunction("0,-1|1,-1|2,-1|3,-1|4,-1|5,-1|6,-1|7,-1|8,-1");
}
Here is a robust solution that's just a simple iteration over a string.
void Main()
{
var xs = "0,-1|1,-1|2,-1|3,-1|4,-1|5,-1|6,-1|7,-1|8,-1";
var stack = new Stack<Pair>();
stack.Push(new Pair());
foreach(var x in xs)
if(x == '|')
stack.Push(new UserQuery.Pair());
else
stack.Peek().Accept(x);
foreach (var x in stack.Reverse())
Console.WriteLine(x);
}
sealed class Pair
{
Action<char> _Accept;
readonly List<char> Line = new List<char>();
readonly List<char> Group = new List<char>();
public Pair()
{
_Accept = x => Line.Add(x);
}
public void Accept(char c)
{
if(c == ',')
_Accept = x => Group.Add(x);
else
_Accept(c);
}
public override string ToString()
{
return "Line:" + new string(Line.ToArray()) + " Group:" + new string(Group.ToArray());
}
}
I have a little algo I wrote to compare the Linq DataContext table to the sql table. It rolls through the properties of the Linq table and gets the CustomeAttributes of the property, (table columns). It's been working for years, but somebody created a table field with a # sign in it, (UPS#). Linq doesn't like such a name for its properties for obvious reasons. So, it has a member of the ColumnAttribute called "Name" to handle the swap. But, I've always used the "Storage" member for my column name. You would think you would just pick up the "Name" member if it's present, but I can't find it to save my life.
This is the code. Any help is very much appreciated.
public static ColumnInfo[] GetColumnsInfo(Type linqTableClass)
{
// Just looking in the loop to see if I missed something.
foreach (var fld in linqTableClass.GetProperties())
{
foreach (var attr in fld.CustomAttributes)
{
foreach (var arg in attr.NamedArguments)
{
if (arg.MemberName == "Name")
Debug.WriteLine(arg.MemberName);
Debug.WriteLine("{0}", arg.MemberName);
}
}
}
var columnInfoQuery =
from field in linqTableClass.GetProperties()
from attribute in field.CustomAttributes
from namedArgument in attribute.NamedArguments
where namedArgument.MemberName == "DbType"
select new ColumnInfo
{
//ColumnName = field.Name,
ColumnName = namedArgument.MemberName,
DatabaseType = namedArgument.TypedValue.Value.ToString(),
};
return columnInfoQuery.ToArray();
}
and this is the property in the Table Class:
[global::System.Data.Linq.Mapping.ColumnAttribute(Name="PEER_UPS#", Storage="_PEER_UPS_", DbType="Char(31) NOT NULL", CanBeNull=false)]
public string PEER_UPS_
{
get
{
return this._PEER_UPS_;
}
set
{
if ((this._PEER_UPS_ != value))
{
this.OnPEER_UPS_Changing(value);
this.SendPropertyChanging();
this._PEER_UPS_ = value;
this.SendPropertyChanged("PEER_UPS_");
this.OnPEER_UPS_Changed();
}
}
}
I couldn't find a pretty way to get this done. For some reason the ColumnAttribute just didn't want to play nice. Ugly as this is, it works.
public class ColumnInfo
{
public string ColumnName { get; set; }
public string DatabaseType { get; set; }
}
public static IEnumerable<ColumnInfo> GetColumnsInfo(Type linqTableClass)
{
Debug.WriteLine(string.Format("Table: {0}", linqTableClass.Name));
/// In-Case this has to grow in the future. Using a list for the arg names to search for.
/// The primary arg should be in position 0 of the array.
string dbTypeArgName = "DbType";
string fldPrimayName = "Storage";
string fldSecondaryName = "Name";
List<string> fldArgnames = new List<string>() { fldPrimayName, fldSecondaryName };
foreach (var fld in linqTableClass.GetProperties())
{
Debug.WriteLine(string.Format("Field Name: {0}", fld.Name));
foreach (var attr in fld.GetCustomAttributesData().Cast<CustomAttributeData>()
.Where(r => r.AttributeType == typeof(ColumnAttribute))
.Where(a => a.NamedArguments
.Select(n => n.MemberName)
.Intersect(fldArgnames)
.Any()))
{
var fldName = attr.NamedArguments.Where(r => r.MemberName == fldSecondaryName).Count() != 0
? attr.NamedArguments.Where(r => r.MemberName == fldSecondaryName).SingleOrDefault().TypedValue.Value.ToString()
: fld.Name;
var fldType = attr.NamedArguments
.Where(r => r.MemberName == dbTypeArgName)
.Select(r => r.TypedValue.Value.ToString())
.SingleOrDefault();
Debug.WriteLine(string.Format("\tTable Field Name {0} Table Type {1}", fldName, fldType));
yield return new ColumnInfo()
{
ColumnName = fldName,
DatabaseType = fldType,
};
}
}
}
and here is what i suggest:
[sorry, my first example was indeed too simplistic]
Here is how i'd do it:
namespace LinqAttributes
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
public class ColumnInfo
{
public string ColumnName { get; set; }
public string DatabaseType { get; set; }
}
public class Test
{
[System.Data.Linq.Mapping.ColumnAttribute(Name = "Whatever", Storage = "Whatever", DbType = "Char(20)", CanBeNull = true)]
public string MyProperty { get; set; }
[System.Data.Linq.Mapping.ColumnAttribute(Name = "PEER_UPS#", Storage = "_PEER_UPS_", DbType = "Char(31) NOT NULL", CanBeNull = false)]
public string PEER_UPS_ { get; set; }
}
internal class Program
{
public static IEnumerable<ColumnInfo> GetColumnsInfo(Type type)
{
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(type))
{
var columnAttribute = descriptor.Attributes
.OfType<System.Data.Linq.Mapping.ColumnAttribute>().SingleOrDefault();
if (columnAttribute != null)
{
yield return new ColumnInfo
{
ColumnName = columnAttribute.Name,
DatabaseType = columnAttribute.DbType
};
}
}
}
private static void Main(string[] args)
{
foreach (var item in GetColumnsInfo(typeof(Test)))
{
Debug.WriteLine(item.ColumnName);
}
}
}
}
Just tested it.
Cheers!
public class City
{
public City() { }
[Column("id_city")]
public int Id { get; private set; }
}
var obj = new City();
var pro = obj.GetType().GetProperties();
string columnAttribute = pro.GetCustomAttributes<ColumnAttribute>().FirstOrDefault().Name;
if(columnAttribute == "id_city") {
//sucess
}
I have this serviceLayer Method that is used by my Web API proyect to return data to clients:
public IEnumerable<Contactos_view> ListarVistaNew(int activos, string filtro, int idSector, int idClient, string ordenar, int registroInic, int registros)
{
using (var myCon = new AdoNetContext(new AppConfigConnectionFactory(EmpresaId)))
{
using (var rep = base_getRep(myCon))
{
return rep.Listar(activos, filtro, idSector, idClient, ordenar, registroInic, registros);
}
}
}
Now the question is: How can I return only desired property of class Contactos_view? This class contains 20 properties, and my Idea is to add a parameter of type string[] Fields so client can select only the desired propeties.
Is it possible? what would be the returned type of ListarVistaNew in that case?
Thank you!
You can dynamically create and populate expando objects.
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
namespace ClientSelectsProperties
{
public class OriginalType
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
class Program
{
// this simulates your original query result - it has all properties
private static List<OriginalType> queryResult = new List<OriginalType> {
new OriginalType { Id = 1, Name = "one", Description = "one description" },
new OriginalType { Id = 2, Name = "two", Description = "two description" }
};
// "hardcoded" property value readers, go crazy here and construct them dynamically if you want (reflection, code generation...)
private static Dictionary<string, Func<OriginalType, object>> propertyReaders = new Dictionary<string, Func<OriginalType, object>> {
{ "Id", t => t.Id },
{ "Name", t => t.Name },
{ "Description", t => t.Description }
};
static void Main(string[] args)
{
// your client only wants Id and Name
var result = GetWhatClientWants(new List<string> { "Id", "Name" });
}
private static List<dynamic> GetWhatClientWants(List<string> propertyNames)
{
// make sure your queryResult is in-memory collection here. Body of this select cannot be executed in the database
return queryResult.Select(t =>
{
var expando = new ExpandoObject();
var expandoDict = expando as IDictionary<string, object>;
foreach (var propertyName in propertyNames)
{
expandoDict.Add(propertyName, propertyReaders[propertyName](t));
}
return (dynamic)expando;
}).ToList();
}
}
}
I am writing a class that reads different kinds of CSV files. It picks out the important information based on Model classes, where the properties of the model class are the column names that I want to grab. For example, I could have an OutlookModel with columns FromAddress and ToAddress. Or I could have a SalesforceModel with totally different columns.
When the reader class parses through the rows and columns, it loads up the cells into an instance of the model class. In the code below, the argument className = OutlookModel. The most relevant lines of code here are the signature and the return...
protected void MapColumns(string row, string className, List<OutlookModel> list)
{
string[] cols = row.Split(',');
// create a model to save the important columns
var model = Activator.CreateInstance(nameSpace, nameSpace + className);
int j = 0;
if (cols.Length > 0)
{
foreach (var c in cols)
{
// is this column index one of our important columns?
if (Ordinals.ContainsKey(j))
{
// this is a column we care about, so set the model property
model.GetType().GetProperty(Ordinals[j]).SetValue(model, c);
}
j++;
}
}
list.Add(model);
}
The problem I am having is the collection of model objects. If I define the object as List< OutlookModel > in the arguments, then the method is not extensible. If I define it as List< object >, then (i think) I have to cast the inside list to use my properties which are all different between the models.
I am fairly new to C#. Is there a better way to capture these different model types into a list/array/collection/whatever so that I can then apply logic to the lists?
So first of all i suggest to add a custom attribute to mark the properties you want to read from the csv, so you don't run into any problem when you have to add something later and you don't have to rely on too many magic strings. Here is my test setup:
class ReadFromCsvAttribute : Attribute { }
class OutlookModel
{
public int DontSetThisValueFromCsv { get; set; }
[ReadFromCsv]
public string FromAddress { get; set; }
[ReadFromCsv]
public string ToAddress { get; set; }
}
class SalesForceModel
{
[ReadFromCsv]
public string Name { get; set; }
[ReadFromCsv]
public string Age { get; set; }
}
static void Main(string[] args)
{
string outlookSample = "Id,FromAddress,ToAddress,Useless\r\n" +
"1,a#b.com,c#d.com,asdf\r\n" +
"3,y#z.com,foo#bar.com,baz";
string salesForceSample = "Id,Name,Age\r\n" +
"1,John,30\r\n" +
"2,Doe,100";
var outlook = ReadFromCsv<OutlookModel>(outlookSample);
var salesForce = ReadFromCsv<SalesForceModel>(salesForceSample);
}
I put together this generic method to read whatever model you want from the data:
static List<T> ReadFromCsv<T>(string data)
{
var objs = new List<T>();
var rows = data.Split(new[] {"\r\n"}, StringSplitOptions.None);
//create index, header dict
var headers = rows[0].Split(',').Select((value, index) => new {value, index})
.ToDictionary(pair => pair.index, pair => pair.value);
//get properties to find and cache them for the moment
var propertiesToFind = typeof (T).GetProperties().Where(x => x.GetCustomAttributes<ReadFromCsvAttribute>().Any());
//create index, propertyinfo dict
var indexToPropertyDict =
headers.Where(kv => propertiesToFind.Select(x => x.Name).Contains(kv.Value))
.ToDictionary(x => x.Key, x => propertiesToFind.Single(p => p.Name == x.Value));
foreach (var row in rows.Skip(1))
{
var obj = (T)Activator.CreateInstance(typeof(T));
var cells = row.Split(',');
for (int i = 0; i < cells.Length; i++)
{
if (indexToPropertyDict.ContainsKey(i))
{
//set data
indexToPropertyDict[i].SetValue(obj, cells[i]);
}
}
objs.Add(obj);
}
return objs;
}
Here's another sample. Since you're new to c#, I've avoided linq and extension methods as much as possible. Just copy it into a console app and run.
Also, I like theHennyy recommendation of using .net attributes to describe a class but only if you have full control of your ecosystem.
public class Account
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class LastNameAccount
{
public string LastName { get; set; }
public string Address { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
Test1();
}
private static void Test1()
{
/*
* defines the result of your CSV parsing.
*/
List<string> csvColumns = new List<string> { "FirstName", "LastName" };
List<List<string>> csvRows = new List<List<string>>() {
new List<string>(){"John","Doe"},
new List<string>(){"Bill", "Nie"}
};
//Map the CSV files to Account type and output it
var accounts = Map<Account>(csvColumns, csvRows);
if (accounts != null)
{
foreach (var a in accounts)
{
Console.WriteLine("Account: {0} {1}", a.FirstName, a.LastName);
}
}
//Map the CSV files to LastNameAccount type and output it
var accounts2 = Map<LastNameAccount>(csvColumns, csvRows);
if (accounts2 != null)
{
foreach (var a in accounts2)
{
Console.WriteLine("Last Name Account: {0} {1}", a.LastName, a.Address);
}
}
}
private static List<T> Map<T>(List<string> columns, List<List<string>> rows)
where T : class, new()
{
//reflect the type once and get valid columns
Type typeT = typeof(T);
Dictionary<int, PropertyInfo> validColumns = new Dictionary<int, PropertyInfo>();
for (int columnIndex = 0; columnIndex < columns.Count; columnIndex++)
{
var propertyInfo = typeT.GetProperty(columns[columnIndex]);
if (propertyInfo != null)
{
validColumns.Add(columnIndex, propertyInfo);
}
}
//start mapping to T
List<T> output = null;
if (validColumns.Count > 0)
{
output = new List<T>();
foreach (var row in rows)
{
//create new T
var tempT = new T();
//populate T's properties
foreach (var col in validColumns)
{
var propertyInfo = col.Value;
var columnIndex = col.Key;
propertyInfo.SetValue(tempT, row[columnIndex]);
}
//add it
output.Add(tempT);
}
}
return output;
}
}
I have created a method which will create dynamic object list from an object list according to property list. In this case I have completed such task using Expandoobject. But I have failed to create distinct list of such expando object list. Please visit the following fidder and see my code.
public class Program
{
public static void Main()
{
var _dynamicObjectList = new List<Student>();
for (int i = 0; i < 5; i++)
{
_dynamicObjectList.Add(new Student { ID = i, Name = "stu" + i, Address = "address" + i, AdmissionDate = DateTime.Now.AddDays(i) , Age=15, FatherName="Jamal"+i, MotherName = "Jamila"+i});
}
//create again for checking distinct list
for (int i = 0; i < 5; i++)
{
_dynamicObjectList.Add(new Student { ID = i, Name = "stu" + i, Address = "address" + i, AdmissionDate = DateTime.Now.AddDays(i), Age = 15, FatherName = "Jamal" + i, MotherName = "Jamila" + i });
}
// var returnList = test2.GetDdlData<Object>(_dynamicObjectList, "ID,Name,Address");
// var returnList = test2.GetDdlData<Object>(_dynamicObjectList, "ID,FatherName,Address");
var returnList = test2.GetDdlData<Object>(_dynamicObjectList, "ID,Name,FatherName,MotherName,Age");
string strSerializeData = JsonConvert.SerializeObject(returnList);
Console.WriteLine(strSerializeData);
Console.ReadLine();
}
}
public class Student
{
public int? ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public DateTime AdmissionDate { get; set; }
public string FatherName { get; set; }
public string MotherName { get; set; }
public int Age { get; set; }
}
public static class test2
{
public static IList GetDdlData<T>(this IEnumerable<T> source, string userParams)
{
try
{
List<string> otherProperties = userParams.Split(',').ToList();
Dictionary<string, PropertyInfo> parentPropertyInfo = new Dictionary<string, PropertyInfo>();
Dictionary<string, Type> parentType = new Dictionary<string, Type>();
var dynamicObjectList = (from k in source select k).ToList();
if (dynamicObjectList.Count() > 0)
{
//if parentField exists then system will handle parent property
if (otherProperties.Count > 0)
{
foreach (string otherProperty in otherProperties)
{
//get parent field property info
PropertyInfo _info = dynamicObjectList[0].GetType().GetProperty(otherProperty);
parentPropertyInfo.Add(otherProperty, _info);
//get parent field propertyType
Type pType = Nullable.GetUnderlyingType(_info.PropertyType) ?? _info.PropertyType;
parentType.Add(otherProperty, pType);
}
}
}
//return List
IList<object> objList = new List<object>();
foreach (T obj in source)
{
var dynamicObj = new ExpandoObject() as IDictionary<string, Object>;
foreach (string otherProperty in otherProperties)
{
PropertyInfo objPropertyInfo = parentPropertyInfo.FirstOrDefault(m => m.Key == otherProperty).Value;
Type objPropertyType = parentType.FirstOrDefault(m => m.Key == otherProperty).Value;
Object data = (objPropertyInfo.GetValue(obj, null) == null) ? null : Convert.ChangeType(objPropertyInfo.GetValue(obj, null), objPropertyType, null);
dynamicObj.Add(otherProperty, data);
}
objList.Add(dynamicObj);
}
var returnUniqList = objList.Distinct().ToList();
return returnUniqList;
}
catch (Exception ex)
{
throw ex;
}
}
}
https://dotnetfiddle.net/hCuJwD
Just add the following code in the code block.
foreach(var objTemp in objList) {
bool isNotSimilar = true;
foreach(string property in otherProperties) {
//get sending object property data
object tempFValue = (objTemp as IDictionary < string, Object > )[property];
//get current object property data
object tempCValue = (dynamicObj as IDictionary < string, Object > )[property];
if (!tempFValue.Equals(tempCValue)) {
isNotSimilar = false;
break;
}
}
if (isNotSimilar) {
isDuplicate = true;
break;
}
}
DOTNETFIDDLE