Set Children Properties with Custom Attribute in Parent Class - c#

I am creating a Log Parsing tool, that is parsing a CSV File into individual classes that derive from a root class. However, it is taking a long time to define the individual classes and to set their individual properties in each class, since there is hundreds of different types of logs. The thing I did notice is that it is all pretty much exactly the same thing and wanted to see if there was a way to speed things up and do something along the lines of how LINQ to DB does things and add some logic in to automatically set properties based on information from Attributes.
Below is an example of what I am working with and an idea on how things should work.
class Program
{
static void Main(string[] args)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>
{
{"key", "Stack Overflow"},
{"item1", "Test"},
{"item2", "Sample"},
{"item3", "3"}
};
Example example = new Example(dictionary);
Console.WriteLine(example.LogKey); //Stack Overflow
Console.WriteLine(example.Item1); //Test
Console.WriteLine(example.Item2); //
Console.WriteLine(example.Item3); //3
Console.ReadKey();
}
}
[AttributeUsage(AttributeTargets.Property)]
class LogItem : Attribute
{
public LogItem(string key)
{
Key = key;
}
public string Key { get; private set; }
public bool Ignore { get; set; }
}
class Log
{
public Log(Dictionary<string, string> items)
{
Dictionary = items;
}
public Dictionary<string, string> Dictionary { get; private set; }
[LogItem("key")]
public string LogKey { get; set; }
}
class Example : Log
{
public Example(Dictionary<string, string> items) : base(items)
{
}
[LogItem("item1")]
public string Item1 { get; set; }
[LogItem("item2", Ignore = true)]
public string Item2 { get; set; }
[LogItem("item3")]
public int Item3 { get; set; }
}
All of my data is comming through as a string unfortunately so it would be a good idea to get the type of the property and converting the string to that. Not important right now for this question since I can do that on my own.
Does anyone have an idea on how to make something like this work? If possible could something like this be done in the Parent Class to allow the Child Class to set the properties with the Attribute Ignore == true on it's own.

I was able to come up with the following after looking into how LINQ to CSV works.
static void ExtractData(Log log)
{
List<PropertyInfo> propertyInfos =
log.GetType()
.GetProperties()
.Where(
p => p.GetCustomAttributes(typeof (LogItem), true).Any(logItem => !((LogItem) logItem).Ignore))
.ToList();
foreach (var propertyInfo in propertyInfos)
{
LogItem logItem = (LogItem)propertyInfo.GetCustomAttributes(typeof(LogItem), true).First();
if(!log.Dictionary.ContainsKey(logItem.Key))
continue;
TypeConverter typeConverter = TypeDescriptor.GetConverter(propertyInfo.PropertyType);
MethodInfo parseNumberMethod = propertyInfo.PropertyType.GetMethod("Parse",
new[] { typeof(String), typeof(NumberStyles), typeof(IFormatProvider) });
MethodInfo parseExactMethod = propertyInfo.PropertyType.GetMethod("ParseExact",
new[] { typeof(string), typeof(string), typeof(IFormatProvider) });
Object objValue = null;
if (typeConverter.CanConvertFrom(typeof(string)))
{
objValue = typeConverter.ConvertFromString(null, CultureInfo.CurrentCulture, log.Dictionary[logItem.Key]);
Debug.WriteLine("TypeConverter - " + propertyInfo.Name);
}
else if (parseExactMethod != null)
{
objValue =
parseExactMethod.Invoke(
propertyInfo.PropertyType,
new Object[]
{
log.Dictionary[logItem.Key],
logItem.OutputFormat,
CultureInfo.CurrentCulture
});
}
else if (parseNumberMethod != null)
{
objValue =
parseNumberMethod.Invoke(
propertyInfo.PropertyType,
new Object[]
{
log.Dictionary[logItem.Key],
logItem.NumberStyles,
CultureInfo.CurrentCulture
});
}
else
{
objValue = log.Dictionary[logItem.Key];
}
PropertyInfo goodPropertyInfo = propertyInfo.DeclaringType.GetProperty(propertyInfo.Name);
goodPropertyInfo.SetValue(log, objValue, null);
}
}

Have you seen LinqToCSV? You create classes for each of the types of log, adding inheritance etc and describe the columns using attributes.
here's an example of how simple your code would become.
IEnumerable<ManualInputFormat> MapFileToRows(Stream input)
{
var csvDescriptor = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
var context = new CsvContext();
return context.Read<InputFormat>(new StreamReader(input), csvDescriptor);
}
where InputFormat is your destination attribute decorated POCO
http://www.codeproject.com/Articles/25133/LINQ-to-CSV-library

Related

C# Linq OrderBy Reflection with . deliminated string

I have a need to use a delimited string in order by. EG "Product.Reference".
I seem to be having trouble as the result is ordered the same way it was before the method was called.
For example I have this xUnit test that shows my issue.
The asserts show that the order is still the same.
EDIT:
to be clear, I am not testing Order by, but the method PathToProperty.
This test is for demonstration purposes only.
As you can see from the test I am using reflection in method private static object PathToProperty(object t, string path) So I am assuming I am doing something wrong in there?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Xunit;
namespace Pip17PFinanceApi.Tests.Workers
{
public class InputViewModel
{
public List<OrderViewModel> OrderViewModels { get; set; }
}
public class OrderViewModel
{
public Product Product { get; set; }
public decimal Price { get; set; }
}
public class Product
{
public string Description { get; set; }
public string Reference { get; set; }
}
public class OrderByWithReflection
{
[Fact]
public void OrderByTest()
{
//Arrrange
var model = new InputViewModel
{
OrderViewModels = new List<OrderViewModel>
{
new OrderViewModel{
Product = new Product
{
Reference = "02"
}
},
new OrderViewModel{
Product = new Product
{
Reference = "03"
}
},
new OrderViewModel{
Product = new Product
{
Reference = "01"
}
},
new OrderViewModel{
Product = new Product
{
Reference = "04"
}
},
}
};
//Act
var query = model.OrderViewModels.OrderBy(t => PathToProperty(t, "Product.Reference"));
var result = query.ToList();
//Assert
Assert.Equal("01", result[0].Product.Reference);
Assert.Equal("02", result[1].Product.Reference);
Assert.Equal("03", result[2].Product.Reference);
Assert.Equal("04", result[3].Product.Reference);
}
private static object PathToProperty(object t, string path)
{
Type currentType = t.GetType();
foreach (string propertyName in path.Split('.'))
{
PropertyInfo property = currentType.GetProperty(propertyName);
t = property;
currentType = property.PropertyType;
}
return t;
}
}
}
Your PathToProperty isn't correct. Think about it's return value - the last time through, you set t = property and then return t. But property is a PropertyInfo, so you are just comparing identical objects in the OrderBy.
I have a similar extension method I use:
public static object GetPropertyPathValue(this object curObject, string propsPath) {
foreach (var propName in propsPath.Split('.'))
curObject = curObject.GetType().GetProperty(propName).GetValue(curObject);
return curObject;
}
If used in place of your PathToProperty method, the OrderBy will work.
var query = model.OrderViewModels.OrderBy(t => t.GetPropertyPathValue("Product.Reference"));
You could update your method to be something like:
private static object PathToProperty(object curObject, string path) {
foreach (string propertyName in path.Split('.')) {
var property = curObject.GetType().GetProperty(propertyName);
curObject = property.GetValue(curObject);
}
return curObject;
}
for the same result.
PS Actually, using some other extension methods and LINQ, my normal method handles properties or fields:
public static object GetPathValue(this object curObject, string memberPath)
=> memberPath.Split('.').Aggregate(curObject, (curObject, memberName) => curObject.GetType().GetPropertyOrField(memberName).GetValue(curObject));

Selecting properties for building dynamic SQL queries

I really don't like to hard code the name of properties of my models. So I came up with this code so far. My code is working fine and it does exactly what I want but in an ugly way. I'm pretty sure it will be problematic soon. So any help to improve it and make it work in the right way is appreciated. I'm looking to fine better way to extract selected property names without converting expression body to string. Any change to any part of this class is fine with me. Even changing usage as long as I don't hard code my property names.
What is the better way to extract selected properties name of a model?
Here is my code:
public class Selector<T> : IDisposable
{
Dictionary<string, Func<T, object>> Selectors = new Dictionary<string, Func<T, object>>();
public Selector(params Expression<Func<T, object>>[] Selector)
{
foreach (var select in Selector)
{
//string MemberName = CleanNamesUp(select.Body.ToString());
//Func<T, object> NewSelector = select.Compile();
#region Ugly Part 1
Selectors.Add(CleanNamesUp(select.Body.ToString()), select.Compile());
#endregion
}
}
#region I am Doing This So I can Use Using(var sl = new Selector<T>())
public void Dispose()
{
Selectors.Clear();
Selectors = null;
}
#endregion
#region Ugly Part 2
private string CleanNamesUp(string nameStr)
{
string name = nameStr.Split('.')[1];
if (name.Contains(","))
{
name = name.Split(',')[0];
}
return name;
}
#endregion
public Dictionary<string, object> GetFields(T Item)
{
Dictionary<string,object> SetFieldList = new Dictionary<string, object>();
foreach(var select in Selectors)
{
SetFieldList.Add( select.Key , select.Value(Item));
}
return SetFieldList;
}
public List<Dictionary<string, object>> GetFields(IEnumerable<T> Items)
{
List<Dictionary<string, object>> SetFieldListMain = new List<Dictionary<string, object>>();
foreach (var item in Items)
{
Dictionary<string, object> SetFieldList = new Dictionary<string, object>();
foreach (var select in Selectors)
{
SetFieldList.Add(select.Key, select.Value(item));
}
SetFieldListMain.Add( SetFieldList);
}
return SetFieldListMain;
}
internal List<string> GetKeys()
{
return new List<string>(this.Selectors.Keys);
}
}
This is my model:
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public bool IsEnabled { get; set; }
public bool IsLocked { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime LockedAt { get; set; }
}
And I am using it like this:
User user1 = new User();
user1.Email = "testDev#gmail.com";
user1.UserName = "dora";
user1.Password = "123456";
var UpObject = new Selector<User>( x => x.UserName, x => x.Password, x => x.Email, x => x.IsEnabled );
Dictionary<string,object> result = UpObject.GetFields(user1);
You can avoid parsing the expressions as string if you instead parse them as System.Linq.Expressions.
Full code sample follows, but not exactly for your code, I used DateTime instead of the generic T, adapting should just be find&replace:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace ExprTest
{
class Program
{
static void Main(string[] args)
{
#region Usage
Expression<Func<DateTime, object>> propertySelector = x => x.Day;
Expression<Func<DateTime, object>> methodSelector = x => x.AddDays(1.5);
Expression[] inputSelectors = new Expression[] { propertySelector, methodSelector };
#endregion
//These are your final Selectors
Dictionary<string, Func<DateTime, object>> outputSelectors = new Dictionary<string, Func<DateTime, object>>();
//This would be in your Selector<T> constructor, replace DateTime with T.
//Instead of CleanNamesUp you would decide which part to use by extracting the appropriate Expression argument's Name.
foreach (Expression<Func<DateTime, object>> selectorLambda in inputSelectors)
{
Expression selectorExpression = selectorLambda.Body;
string name = null;
while (string.IsNullOrEmpty(name))
{
switch (selectorExpression)
{
#region Refine expression
//Necessary for value typed arguments, which get boxed by Convert(theStruct, object)
case UnaryExpression unary:
selectorExpression = unary.Operand;
break;
//add other required expression extractions
#endregion
#region Select expression key/name
case MemberExpression fieldOrProperty:
name = fieldOrProperty.Member.Name;
break;
case MethodCallExpression methodCall:
name = methodCall.Method.Name;
break;
//add other supported expressions
#endregion
}
}
outputSelectors.Add(name, selectorLambda.Compile());
}
//Set a breakpoint here to explore the outputSelectors
}
}
}
There could be a library for this, but i don't know about any, except PredicateBuilder for when you need to unify lambda arguments into one lambda expression.
I think maybe you forgot an important keyword 'nameof'. With the keyword, the code will be like this:
class User
{
public string Name { get; set; }
public string Address { get; set; }
public string Tel { get; set; }
}
static Dictionary<string, object> GetFieldsOf<T>(T item, params string[] args)
{
var properties = args.Select(property => typeof(T).GetProperty(property));
return properties.ToDictionary(property => property.Name, property => property.GetValue(item));
}
static void Main(string[] args)
{
var user = new User { Name = "Abel", Address = "Unknown", Tel = "XXX-XXX" };
var result = GetFieldsOf(user, nameof(User.Name), nameof(User.Address));
}
This code will result in some performance problems caused by reflection. But fortunately, you can avoid these by emitting a small segement of code.
//MSIL
ldarg.0
call Property.GetMethod
ret
And replace it with proerpty.GetValue. These code can be generated and cached per type, which is still worthwhile.

How to find a specific value in an IDictionary<string, object>?

This IDictionary<string, object> contains user data I'm logging into mongodb. The issue is the TValue is a complex object. The TKey is simply the class name.
For example:
public class UserData
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Admin NewAdmin { get; set; }
}
public class Admin
{
public string UserName { get; set; }
public string Password { get; set; }
}
Currently, I'm trying to iterate through the Dictionary and compare types but to no avail. Is there a better way of doing this or am I missing the mark?
var argList = new List<object>();
foreach(KeyValuePair<string, object> kvp in context.ActionArguments)
{
dynamic v = kvp.Value;
//..compare types...
}
Just use OfType<>(). You don't even need the key.
public static void Main()
{
var d = new Dictionary<string,object>
{
{ "string", "Foo" },
{ "int", 123 },
{ "MyComplexType", new MyComplexType { Text = "Bar" } }
};
var s = d.Values.OfType<string>().Single();
var i = d.Values.OfType<int>().Single();
var o = d.Values.OfType<MyComplexType>().Single();
Console.WriteLine(s);
Console.WriteLine(i);
Console.WriteLine(o.Text);
}
Output:
Foo
123
Bar
Link to Fiddle

How to iterate through limited set of instance properties?

I have a class with huge number of properties but I want to alter only several of them. Could you suggest me how to implement functionality below:
var model = session.Load<MyType>(id);
foreach(var property in [model.RegistrationAddress, model.ResidenceAddress, model.EmploymentAddress, model.CorrespondenceAddress])
{
// alter each of the given properties...
}
When wrapping it in an object[] you can get all values, but you loose the knowledge of the property behind it.
foreach( var property in
new object[]
{ model.RegistrationAddress
, model.ResidenceAddress
, model.EmploymentAddress
, model.CorrespondenceAddress
}
)
{
// alter each of the given properties...
}
You can use a Dictionary instead:
When wrapping it in an object[] you can get all values, but you loose the knowledge of the property behind it.
foreach( KeyValuePair<string, object> property in
new Dictionary<string, object>
{ { "RegistrationAddress", model.RegistrationAddress}
, { "ResidenceAddress", model.ResidenceAddress } ...
}
)
{
// alter each of the given properties...
}
Ideally, in the next version of c#, you can use nameof:
new Dictionary<string, object>
{ { nameof(RegistrationAddress), model.RegistrationAddress}
, { nameof(ResidenceAddress), model.ResidenceAddress } ...
}
When you need to set the parameters, you can use something like this:
public class GetSet<T>
{
public GetSet(Func<T> get, Action<T> set)
{
this.Get = get;
this.Set = set;
}
public Func<T> Get { get; set; }
public Action<T> Set { get; set; }
}
Call it like this:
ClassX x = new ClassX();
foreach (var p in new GetSet<string>[] { new GetSet<string>(() => { return x.ParameterX; }, o => { x.ParameterX = o; }) })
{
string s = p.Get();
p.Set("abc");
}

How to Map Object / Class Property to Dictionary When Object Property And Dictionary Key Names Are Different?

I am calling Bloomberg Server API (for Stock Market Data) and getting Data back in a Dictionary<string, object> Where the Key to the dictionary is the Field Name on Bloomberg's side, and the object contains the data value from Bloomberg and can be string, decimal, DateTime, boolean etc.
After I get the Bloomberg Data, I need to populate my strong type entities / classes with the values returned. Depending on what Field Names I send in my request to bloomberg, the returned Dictionary could have different key values. The problem I am having is, the bloomberg field name and my .net entity's property names don't match, so I am not sure I can do this mapping using AutoMapper or a similar library.
I also tried using a Tuple<string,string,object> where the 1st tuple item is the bloomberg field name, the 2nd tuple item is my entity's property name and the 3rd tuple item is the data value returned from bloomberg. That is not working out either (so far), so I am wondering if there is a simple straight-forward way of maintaining this bloombergfield<-->EntityProperty mapping and populate the Entity's value using the Bloomberg Data Value for the respective field. A Generic (i.e. using C# Generics) solution would be even better!
I have pasted below sample console app code so you can paste it and try it out. The 2 dictionaries, 1 for stockdata and other for bonddata have fake data, but you get the idea. I have also added comments below to re-iterate what I am trying to accomplish.
Thanks!!
namespace MapBBFieldsToEntityProperties
{
using System;
using System.Collections.Generic;
class Program
{
public class StockDataResult
{
public string Name { get; set; }
public decimal LastPrice { get; set; }
public DateTime SettlementDate { get; set; }
public decimal EPS { get; set; }
}
public class BondDataResult
{
public string Name { get; set; }
public string Issuer { get; set; }
public decimal Duration { get; set; }
public DateTime YieldToMaturity { get; set; }
}
static void Main(string[] args)
{
// Data Coming from Bloomberg.
// Dictionary Key is the Bloomberg Data Field Name.
// Dictionary Object is the Value returns and can be any .Net primitive Type
// Sample Data returned for a Stock Query to Bloomberg
Dictionary<string, object> dctBloombergStockData
= new Dictionary<string, object>
{
{ "NAME", "IBM" },
{ "PX_LAST", 181.30f },
{ "SETTLE_DT", "11/25/2013" } // This is Datetime value
};
// Sample Data returned for a Bond Query to Bloomberg
Dictionary<string, object> dctBloombergBondData =
new Dictionary<string, object>
{
{ "NAME", "IBM" },
{ "ISSUE_ORG","IBM Corp" },
{ "DURATION", 4.430f },
{ "YLD_TO_M", 6.456f }
};
// This is my Stock Entity
StockDataResult stockData = new StockDataResult();
// This is my Bond Entity
BondDataResult bondData = new BondDataResult();
// PROBLEM STATEMENT:
//
// Need to somehow Map the Data returned from Bloomberg into the
// Corresponding Strong-typed Entity for that Data Type.
// i.e.
// map dctBloombergStockData to stockData Entity instance as follows
//
// dctBloombergStockData."NAME" Key <--------> stockData.Name Property so that
// dctBloombergStockData["NAME"] value of "IBM" can be assigned to stockData.Name
//
// dctBloombergStockData."PX_LAST" Key <--------> stockData.LastPrice Property so that
// dctBloombergStockData["PX_LAST"] value 181.30f can be assigned to stockData.LastPrice value.
// ....
// .. you get the idea.
// Similarly,
// map dctBloombergBondData Data to bondData Entity instance as follows
//
// dctBloombergBondData."NAME" Key <--------> bondData.Name Property so that
// dctBloombergBondData["NAME"] value of "IBM" can be assigned to bondData.Name property's value
//
// dctBloombergBondData."ISSUE_ORG" Key <--------> bondData.Issuer Property so that
// dctBloombergBondData["ISSUE_ORG"] value 181.30f can be assigned to bondData.Issuer property's value.
//
// dctBloombergBondData."YLD_TO_M" Key <--------> bondData.YieldToMaturity Property so that
// dctBloombergBondData["YLD_TO_M"] value 181.30f can be assigned to bondData.YieldToMaturity property's value.
}
}
}
I am sure quite a few improvements are possible, but this is one way of specifying a mapping and using that map.
class Program
{
public class Mapper<TEntity> where TEntity : class
{
private readonly Dictionary<string, Action<TEntity, object>> _propertyMappers = new Dictionary<string, Action<TEntity, object>>();
private Func<TEntity> _entityFactory;
public Mapper<TEntity> ConstructUsing(Func<TEntity> entityFactory)
{
_entityFactory = entityFactory;
return this;
}
public Mapper<TEntity> Map<TProperty>(Expression<Func<TEntity, TProperty>> memberExpression, string bloombergFieldName, Expression<Func<object, TProperty>> converter)
{
var converterInput = Expression.Parameter(typeof(object), "converterInput");
var invokeConverter = Expression.Invoke(converter, converterInput);
var assign = Expression.Assign(memberExpression.Body, invokeConverter);
var mapAction = Expression.Lambda<Action<TEntity, object>>(
assign, memberExpression.Parameters[0], converterInput).Compile();
_propertyMappers[bloombergFieldName] = mapAction;
return this;
}
public TEntity MapFrom(Dictionary<string, object> bloombergDict)
{
var instance = _entityFactory();
foreach (var entry in bloombergDict)
{
_propertyMappers[entry.Key](instance, entry.Value);
}
return instance;
}
}
public class StockDataResult
{
public string Name { get; set; }
public decimal LastPrice { get; set; }
public DateTime SettlementDate { get; set; }
public decimal EPS { get; set; }
}
public static void Main(params string[] args)
{
var mapper = new Mapper<StockDataResult>()
.ConstructUsing(() => new StockDataResult())
.Map(x => x.Name, "NAME", p => (string)p)
.Map(x => x.LastPrice, "PX_LAST", p => Convert.ToDecimal((float)p))
.Map(x => x.SettlementDate, "SETTLE_DT", p => DateTime.ParseExact((string)p, "MM/dd/yyyy", null));
var dctBloombergStockData = new Dictionary<string, object>
{
{ "NAME", "IBM" },
{ "PX_LAST", 181.30f },
{ "SETTLE_DT", "11/25/2013" } // This is Datetime value
};
var myStockResult = mapper.MapFrom(dctBloombergStockData);
Console.WriteLine(myStockResult.Name);
Console.WriteLine(myStockResult.LastPrice);
Console.WriteLine(myStockResult.SettlementDate);
}
}
As you have said your self you need a mapping table. You can create a static read only dictionary in you type that would map each key filed returned from Bloomberg to property in your strongly typed class.
Here is how I would do it.
PS: I used linqpad to test.
PPS: You can add as many mapper(s) to the dictionary as need be. You also need fast-member to run this code.
void Main()
{
var dctBloombergStockData = new Dictionary<string, object>
{
{ "NAME", "IBM" },
{ "PX_LAST", 181.30f },
{ "SETTLE_DT", "11/25/2013" } // This is Datetime value
};
StockDataResult.FromBloombergData(dctBloombergStockData);
}
// Define other methods and classes here
interface IMapper
{
string PropertyName { get; }
object Parse(object source);
}
class Mapper<T, TResult> : IMapper
{
private Func<T, TResult> _parser;
public Mapper(string propertyName, Func<T, TResult> parser)
{
PropertyName = propertyName;
_parser = parser;
}
public string PropertyName { get; private set; }
public TResult Parse(T source)
{
source.Dump();
return _parser(source);
}
object IMapper.Parse(object source)
{
source.Dump();
return Parse((T)source);
}
}
public class StockDataResult
{
private static TypeAccessor Accessor = TypeAccessor.Create(typeof(StockDataResult));
private static readonly Dictionary<string, IMapper> Mappers = new Dictionary<string, IMapper>(StringComparer.CurrentCultureIgnoreCase){
{ "NAME", new Mapper<string, string>("Name", a => a) },
{ "PX_LAST", new Mapper<float, decimal>("LastPrice", a => Convert.ToDecimal(a)) },
{ "SETTLE_DT", new Mapper<string, DateTime>("SettlementDate", a => DateTime.ParseExact(a, "MM/dd/yyyy", null)) }
};
protected StockDataResult()
{ }
public string Name { get; set; }
public float LastPrice { get; set; }
public DateTime SettlementDate { get; set; }
public decimal EPS { get; set; }
public static StockDataResult FromBloombergData(Dictionary<string, object> state)
{
var result = new StockDataResult();
IMapper mapper;
foreach (var entry in state)
{
if(!Mappers.TryGetValue(entry.Key, out mapper))
{ continue; }
Accessor[result, mapper.PropertyName.Dump()] = mapper.Parse(entry.Value);
}
return result;
}
}
How about:
stockData.Name = dctBloombergStockData["NAME"];
stockData.LastPrice = dctBloombergStockData["PX_LAST"]
//and so on...

Categories

Resources