Better Way to Iterate Class Properties - c#

I have the following class called HIP
using System;
namespace Shared
{
public class HIP
{
public HIP ()
{
}
public double data_source { get; set; }
public string hid { get; set; }
public double wid { get; set; }
public double psn { get; set; }
}
}
And I got the oData and adding each properties to the List as follows:
var client= new ODataClient(settings);
var packages =await client.For("HIP").FindEntriesAsync();
protected List<HIP> hcp = new List<HIP>();
foreach (var package in packages)
{
hcp.Add(new HIP {wid=Convert.ToSingle(package["wid"])});
hcp.Add(new HIP {hid=package["hid"].ToString()});
hcp.Add(new HIP {psn=Convert.ToSingle(package["psn"])});
hcp.Add(new HIP {data_source=Convert.ToSingle(package["data_source"])});
}
My question is how to get foreach operation in optimal/better way. Now, I have 4-5 properties and I could type each property names as follows package["wid"],package["hid"],package["psn"],package["data_source"]; however what if I have a tens of properties. I would like to know is there a better way to iterate.

You can use reflection to do something along the lines of:
var hcp = new List<HIP>();
var hipProperties = typeof(HIP).GetProperties();
hcp.AddRange(hipProperties.Select(prop =>
{
var hip = new HIP();
prop.SetValue(hip, Convert.ChangeType(package[prop.Name], prop.PropertyType), null);
return hip;
}).ToList();
The above code will generate a list of HIP objects with only a single property set on each. I believe you might want to create a single HIP object with all its properties set for each package, like this:
var client = new ODataClient(settings);
var packages = await client.For("HIP").FindEntriesAsync();
var hcp = new List<HIP>();
var properties = typeof(Hip).GetProperties();
foreach (var p in packages)
{
var hip = new HIP();
foreach (var prop in properties)
{
prop.SetValue(hip, Convert.ChangeType(p[prop.Name], prop.PropertyType), null);
}
hcp.Add(hip);
}

Related

Seperation of db connection in seperate class file doesn't work

Rookie here needing help. I'm trying to build a prototype with the neo4j .NET driver using Bolt. My aim with the prototype is building multiple methods for creation and searches in the db, but only one method to connect to the db - here I'm continuously having problems. I've Googled all weekend for examples, tutorials and traversed through the documentation and now I need your help.
Programs.cs
using System;
using DTUneo4jConsoleApp.Db;
namespace DTUneo4jConsoleApp
{
public class Program
{
public static void Main(string[] args)
{
MyProperties something = new MyProperties();
neo4jdb session = new neo4jdb();
session.Run($"CREATE (a:Person {{name:'{something.Name}', title:'{something.Title}'}})");
var result = session.Run($"MATCH (a:Person) WHERE a.name = '{something.Name}' RETURN a.name AS name, a.title AS title");
foreach (var record in result)
{
Console.WriteLine($"{record["title"].As<string>()} {record["name"].As<string>()}");
}
Console.ReadKey();
}
}
public class MyProperties
{
public string Name { get; set; }
public string Title { get; set; }
}
}
db.cs
using Neo4j.Driver.V1;
namespace DTUneo4jConsoleApp.Db
{
public class neo4jdb
{
public static void Connection()
{
using (var driver = GraphDatabase.Driver("bolt://localhost", AuthTokens.Basic("user", "pass")))
using (var session = driver.Session())
{
}
}
}
}
When I instantiate the neo4jdb session = new neo4jdb(); I don't get i.e. the Run() method from the driver.
I hope someone can guide me in the right direction.
I am doing it like this:
public static List<IStatementResult> ExecuteCypher(List<Statement> statements)
{
List<IStatementResult> results = new List<IStatementResult>();
using (var driver = GraphDatabase.Driver("bolt://localhost", AuthTokens.Basic("user", "pass")))
{
using (var session = driver.Session())
{
using (var tx = session.BeginTransaction())
{
foreach (var statement in statements)
{
results.Add(tx.Run(statement));
}
tx.Success();
}
}
}
return results;
}
usage:
MyProperties something = new MyProperties();
var createCypher = new Statement($"CREATE (a:Person {{name:'{something.Name}', title:'{something.Title}'}})");
var matchCypher = new Statement($"MATCH (a:Person) WHERE a.name = '{something.Name}' RETURN a.name AS name, a.title AS title");
var statements = new List<Statement>();
statements.Add(createCypher);
statements.Add(matchCypher);
var results = ExecuteCypher(statements);
//you can now query result for each statement or
//your query your desired result
foreach (var record in results.Last())
{
Console.WriteLine($"{record["title"].As<string>()} {record["name"].As<string>()}");
}
In this way I can also create multiple records in a single transaction and get the result of all those as well.

How to map two list with automapper?

I have two classes in the different location.
namespace IVR.MyEndpointApi.POCO
{
[Table("MyServiceUrl")]
public class MyURL
{
[Key]
[Column("FacilityID")]
public int FacilityId { get; set; }
[Column("Url")]
public string Url { get; set; }
}
}
namespace OpsTools.Models
{
public class MyServiceEndpoint
{
public int FacilityId { get; set; }
public string Url { get; set; }
}
}
In another method, I get the list and want to convert then return it as the desired type. I manually do it as below:
public List<MyServiceEndpoint> GetAllUrls()
{
var management = GetMyEndpointManagement();
var list = management.GetAllUrls();
var urlList = new List<MyServiceEndpoint>();
foreach (var item in list)
{
// the type of item is MyURL
var MyUrl = new MyServiceEndpoint();
myUrl.FacilityId = item.FacilityId;
myUrl.Url = item.Url;
urlList.Add(myUrl);
}
return urlList;
}
My question: can I apply AutoMapper to it?
EDIT:
I used the code:
var myUrls = management.GetAllUrls();
var urlList = new List<MyServiceEndpoint>();
Mapper.CreateMap<MyServiceEndpoint, MyURL>();
urlList = Mapper.Map<List<MyServiceEndpoint>, List<MyURL>>(myUrls);
Mapper.AssertConfigurationIsValid();
However, it has the error:
Error CS1503 Argument 1: cannot convert from 'System.Collections.Generic.List' to ....
Oops, come on. I change the order, then it works.
From
urlList = Mapper.Map<List<MyServiceEndpoint>, List<MyURL>>(myUrls);
To
urlList = Mapper.Map<List< List<MyURL>,MyServiceEndpoint>>(myUrls);
If you inspect the actual exception (you left the relevant part off), you'll see that Mapper.Map<TSource, TDestination>() tries to map from and to the wrong types.
The full error will read:
cannot convert from System.Collections.Generic.List<MyURL> to System.Collections.Generic.List<MyServiceEndpoint>
Which means that call will actually try to map from List<MyServiceEndpoint>, requiring an argument of that type, which your source list isn't.
Simply switch the types in the Map() call:
urlList = Mapper.Map<List<MyURL>, List<MyServiceEndpoint>>(myUrls);
Or remove the new list creation entirely, move the declaration and use type inference:
var urlList = Mapper.Map<List<MyServiceEndpoint>>(myUrls);

How to save ObservableCollection in Windows Store App?

I am creating Windows Store App based on Split App template. What is the best way to save data from SampleDataSource for later use?
I tried:
Windows.Storage.ApplicationDataContainer roamingSettings = Windows.Storage.ApplicationData.Current.RoamingSettings;
roamingSettings.Values["Data"] = AllGroups;
It throws exception: 'Data of this type is not supported'.
RoamingSettings only supports the runtime data types (with exception of Uri); additionally, there's a limitation as to how much data you can save per setting and in total.
You'd be better off using RoamingFolder (or perhaps LocalFolder) for the storage aspects.
For the serialization aspect you might try the DataContractSerializer. If you have a class like:
public class MyData
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
}
public ObservableCollection<MyData> coll;
then write as follows
var f = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("data.txt");
using ( var st = await f.OpenStreamForWriteAsync())
{
var s = new DataContractSerializer(typeof(ObservableCollection<MyData>),
new Type[] { typeof(MyData) });
s.WriteObject(st, coll);
and read like this
using (var st = await Windows.Storage.ApplicationData.Current.LocalFolder.OpenStreamForReadAsync("data.txt"))
{
var t = new DataContractSerializer(typeof(ObservableCollection<MyData>),
new Type[] { typeof(MyData) });
var col2 = t.ReadObject(st) as ObservableCollection<MyData>;
}

How to cast from a list with objects into another list.

I have an Interface [BindControls] which takes data from GUI and store it into a list „ieis”.
After that, Into another class, which sends this data through WebServices, I want to take this data from „ieis” and put it into required by WS Class fields (bottom is a snippet of code)
This is the interface:
void BindControls(ValidationFrameBindModel<A.B> model)
{
model.Bind(this.mtbxTax, (obj, value) =>
{
var taxa = TConvertor.Convert<double>((string)value, -1);
if (taxa > 0)
{
var ieis = new List<X>();
var iei = new X
{
service = new ServiceInfo
{
id = Constants.SERVICE_TAX
},
amount = tax,
currency = new CurrencyInfo
{
id = Constants.DEFAULT_CURRENCY_ID
}
};
ieis.Add(iei);
}
},"Tax");
}
This is the intermediate property:
//**********
class A
{
public B BasicInfo
{
get;
set;
}
class B
{
public X Tax
{
get;
set;
}
}
}
//***********
This is the class which sends through WS:
void WebServiceExecute(SomeType someParam)
{
//into ‚iai’ i store the data which comes from interface
var iai = base.Params.FetchOrDefault<A>( INFO, null);
var convertedObj = new IWEI();
//...
var lx = new List<X>();
//1st WAY: I tried to put all data from ‚Tax’into my local list ‚lx’
//lx.Add(iai.BasicInfo.Tax); - this way is not working
//2nd WAY: I tried to put data separately into ‚lx’
var iei = new X
{
service = new ServiceInfo
{
id = iai.BasicInfo.Tax.service.id
},
amount = iai.BasicInfo.Tax.amount,
currency = new CurrencyInfo
{
id = iai.BasicInfo.Tax.currency.id
}
};
lx.Add(iei);
// but also is not working
Can you help me please to suggest how to implement a way that will fine do the work (take data from ‚ieis’ and put her into ‚lx’).
Thank you so much
As noted in my comment, it looks like iai.BasicInfo.Tax is null, once you find out why that is null your original Add() (#1) will work.

NHibernate QueryByExample including only certain properties

I have created a custom Property Selector to accept an array in the constructor to say which properties should be included in the search. The approach works well as long as there are no component types, but how do I deal with those? Here is an example:
public class Customer
{
public virtual int Id { get; private set; }
public virtual Name Name { get; set; }
public virtual bool isPreferred { get; set; }
//...etc
}
public class Name
{
public string Title { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Fullname { get; }
}
public class CustomerPropertySelector : Example.IPropertySelector
{
private string[] _propertiesToInclude = { };
public CustomerPropertySelector(string[] propertiesToInclude)
{
this._propertiesToInclude = propertiesToInclude;
}
public bool Include(object propertyValue, String propertyName, NHibernate.Type.IType type)
{
//...Checking for null and zeros etc, excluded for brevity
if (!_propertiesToInclude.Contains(propertyName))
return false;
return true;
}
}
I would like to be able to search by first name, but not necessarily last. The property name is Name however, so both first and last names seem to be part of the same property, and something like Name.Firstname, which would normally work as a criterion, doesn't seem to work here. What would be the best way around that?
EXAMPLE:
Customer exampleCust = new Customer(FirstName: "Owen");
IList<Customer> matchedCustomers = _custRepo.GetByExample(exampleCust, new string[] { "Name.FirstName" });
Given that there are 2 customers in db, only one named "Owen", but both have isPreferred = false, I would like my query to only return the first one. Standard QBE will return both based on isPreferred property.
SOLUTION:
Thank you for the answers, the solution is mostly based on answer by therealmitchconnors, however I couldn't have done it without Mark Perry's answer either.
The trick was to realise that rather than including Name.FirstName property, I actually want to exclude Name.LastName, since QBE only allows us to exclude properties. I used a method adapted from therealmitchconnors's answer to help me determine fully qualified names of properties. Here is the working code:
public IList<T> GetByExample(T exampleInstance, params string[] propertiesToInclude)
{
ICriteria criteria = _session.CreateCriteria(typeof(T));
Example example = Example.Create(exampleInstance);
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
var childProperties = GetChildProperties(prop);
foreach (var c in childProperties)
{
if (!propertiesToInclude.Contains(c))
example.ExcludeProperty(c);
}
}
criteria.Add(example);
return criteria.List<T>();
}
private IEnumerable<string> GetChildProperties(System.Reflection.PropertyInfo property)
{
var builtInTypes = new List<Type> { typeof(bool), typeof(byte), typeof(sbyte), typeof(char),
typeof(decimal), typeof(double), typeof(float), typeof(int), typeof(uint), typeof(long),
typeof(ulong), typeof(object), typeof(short), typeof(ushort), typeof(string), typeof(DateTime) };
List<string> propertyNames = new List<string>();
if (!builtInTypes.Contains(property.PropertyType) && !property.PropertyType.IsGenericType)
{
foreach (var subprop in property.PropertyType.GetProperties())
{
var childNames = GetChildProperties(subprop);
propertyNames = propertyNames.Union(childNames.Select(r => property.Name + "." + r)).ToList();
}
}
else
propertyNames.Add(property.Name);
return propertyNames;
}
I wasn't sure of the best way to determine whether a property was a component class or not, any suggestions on how to improve the code are very welcome.
The following code would replace the logic you are using to populate propertiesToInclude. I changed it from an array to a list so I could use the Add method because I am lazy, but I think you get the picture. This does only work for one sub-level of properties. For n levels you would need to recurse.
List<string> _propertiesToInclude = new List<string>();
Type t;
var props = t.GetProperties();
foreach (var prop in props)
{
if (prop.PropertyType.IsClass)
foreach (var subprop in prop.PropertyType.GetProperties())
_propertiesToInclude.Add(string.Format("{0}.{1}", prop.Name, subprop.Name));
else
_propertiesToInclude.Add(prop.Name);
}
I thought I had something but reading your question again you want to know why the QBE NHibernate code doesn't work with component properties.
I think you need to create a sub-criteria query for the Name part.
Perhaps something like this:
public IList<Customer> GetByExample(Customer customer, string[] propertiesToExclude){
Example customerQuery = Example.Create(customer);
Criteria nameCriteria = customerQuery.CreateCriteria<Name>();
nameCriteria.Add(Example.create(customer.Name));
propertiesToExclude.ForEach(x=> customerQuery.ExcludeProperty(x));
propertiesToExclude.ForEach(x=> nameCriteria.ExcludeProperty(x));
return customerQuery.list();
}
This is an example from the NHibernate Test Project, it shows how to exclude Component properties.
[Test]
public void TestExcludingQBE()
{
using (ISession s = OpenSession())
using (ITransaction t = s.BeginTransaction())
{
Componentizable master = GetMaster("hibernate", null, "ope%");
ICriteria crit = s.CreateCriteria(typeof(Componentizable));
Example ex = Example.Create(master).EnableLike()
.ExcludeProperty("Component.SubComponent");
crit.Add(ex);
IList result = crit.List();
Assert.IsNotNull(result);
Assert.AreEqual(3, result.Count);
master = GetMaster("hibernate", "ORM tool", "fake stuff");
crit = s.CreateCriteria(typeof(Componentizable));
ex = Example.Create(master).EnableLike()
.ExcludeProperty("Component.SubComponent.SubName1");
crit.Add(ex);
result = crit.List();
Assert.IsNotNull(result);
Assert.AreEqual(1, result.Count);
t.Commit();
}
}
Source code link

Categories

Resources