Create a dictionary of a model? - c#

It's quite hard for me to explain this, but I will give it a go.
Objective:
Create a LINQ query that will return a dictionary of data. However it must be a dictionary of the model which I am using.
View Model:
public class ValueBySupplierAndClaimTypeViewModel : ReportViewModel
{
public IQueryable<ValueBySupplierAndClaimTypeModel> ReportData {get; set; }
public TotalValueBySupplierAndClaimTypeModel ReportTotalData { get; set; }
public Dictionary<string, decimal> DictionaryData { get; set; }
public string output { get; set; }
}
Interface:
Dictionary<string, decimal> DictData;
TotalValueBySupplierAndClaimTypeModel GetTotalValueBySupplierAndClaimType(
int ClientID, int ReviewPeriodID, int StatusCategoryID);
SQL Repository:
public TotalValueBySupplierAndClaimTypeModel GetTotalValueBySupplierAndClaimType(int ClientID, int ReviewPeriodID, int StatusCategoryID)
{
var rt =
this.GetValueBySupplierAndClaimType(ClientID, ReviewPeriodID, StatusCategoryID);
TotalValueBySupplierAndClaimTypeModel x = new TotalValueBySupplierAndClaimTypeModel()
{
NormalTotal = rt.Sum(c=>c.Normal) ?? 0,
QueryTotal = rt.Sum( c => c.Query) ?? 0,
StrongTotal = rt.Sum( c => c.Strong) ?? 0
};
return x;
}
I'm really not sure how to do this. Can anybody help?

I have this function that converts an object to a dictionary. It gets all the properties of the class, as the dictionary's keys. May be you can modify it to meet your needs:
public Dictionary<string, object> ConvertClassToDict(object classToConvert)
{
Dictionary<string, object> result = new Dictionary<string, object>();
PropertyInfo[] properties = classToConvert.GetType().GetProperties();
List<string> propertiesNames = properties.Select(p => p.Name).ToList();
foreach (var propName in propertiesNames)
{
PropertyInfo property = properties.First(srcProp => srcProp.Name == propName);
var value = property.GetValue(classToConvert, null);
result.Add(propName, value);
}
return result;
}
The argument classToConvert, is just an instance of any class.

Similar to #lukiller's answer, but with LINQ:
public Dictionary<string, object> MapToDictionary(object instance)
{
if(instance == null) return null;
return instance.GetType()
.GetProperties()
.ToDictionary(p => p.Name,
p => p.GetValue(instance));
}
For example, let's suppose we have the following class:
public class User
{
public string Username { get; set; }
public string Password { get; set; }
}
We can print it like this (one line):
MapToDictionary(new User()
{
Username = "mcicero",
Password = "abc123"
}).ToList().ForEach(i => Console.WriteLine("{0}: {1}", i.Key, i.Value));
This prints out:
Username: mcicero
Password: abc123

Related

C#, filter custom attribute property values

Consider a class:
public class Dog
{
[Key]
[TableField(Name= "Jersey", Inoculated = false)]
public string Param1{ get; set; }
[TableField(Name= "Daisy", Inoculated = true)]
public string Param2{ get; set; }
[TableField(Name= "Jeremy", Inoculated = true)]
public string Param3{ get; set; }
}
And an attribute class:
public sealed class TableField : Attribute
{
public string Name { get; set; }
public bool Inoculated { get; set; }
}
This is a bit far from real-life example but what I need is to select all TableField.Name property values from typeof(Dog) (default class value) where TableField.Inoculated == true.
Best attempt, don't know where to go from here:
var names = typeof(Dog).GetProperties()
.Where(r => r.GetCustomAttributes(typeof(TableField), false).Cast<TableField>()
.Any(x => x.Inoculated));
If you need to select from properties by attributes, the following example may be useful to you.
var dogType = typeof(Dog);
var names = dogType.GetProperties()
.Where(x => Attribute.IsDefined(x, typeof(TableField)))
.Select(x => x.GetCustomAttribute<TableField>())
.Where(x => x.Inoculated == true)
.Select(x=>x.Name);
By using System.Reflection, you could do something like this:
public static Dictionary<string, string> GetDogNames()
{
var namesDict = new Dictionary<string, string>();
var props = typeof(Dog).GetProperties();
foreach (PropertyInfo prop in props)
{
object[] attrs = prop.GetCustomAttributes(true);
foreach (object attr in attrs)
{
if (attr is TableField tableField && tableField.Inoculated)
{
string propName = prop.Name;
string auth = tableField.Name;
namesDict.Add(propName, auth);
}
}
}
return namesDict;
}

How to map an ExpandoObject to a type?

I have a dynamic ExpandoObject result with the following key-value pairs:
{ id: "1" }
{ product_name: "some name" }
{ product_category: "some category" }
And I have a class:
public class Product
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("productName")]
public string ProductName { get; set; }
[JsonProperty("productCategory")]
public string ProductCategory { get; set; }
}
So can I map the ExpandoObject to this class as a new object? (The ExpandoObject properties come from database columns.)
You could make a helper method like this:
public static T FromExpando<T>(ExpandoObject expando) where T : class, new()
{
if (expando == null) return null;
var properties = typeof(T)
.GetProperties()
.Where(pi => pi.CanWrite && !pi.GetIndexParameters().Any())
.ToDictionary(pi => pi.Name.ToLower());
T obj = new T();
foreach (var kvp in expando)
{
var name = kvp.Key.ToLower().Replace("_", "");
var val = kvp.Value;
if (val != null &&
properties.TryGetValue(name, out PropertyInfo prop) &&
prop.PropertyType.IsAssignableFrom(val.GetType()))
{
prop.SetValue(obj, val);
}
}
return obj;
}
Then call it like this:
Product prod = FromExpando<Product>(expando);
Fiddle: https://dotnetfiddle.net/TUgaW5

How to use attribute to map properties

I have the following code.
public class SyncProperty : Attribute
{
public readonly string PropertyName;
public SyncProperty(string propertyName)
{
this.PropertyName = propertyName;
}
}
public class SyncContact
{
[SyncProperty("first_name")]
public string FirstName { get; set; }
[SyncProperty("last_name")]
public string LastName { get; set; }
[SyncProperty("phone")]
public string Phone { get; set; }
[SyncProperty("email")]
public string Email { get; set; }
}
I need to create an instance of my SyncContact such as
var contact = new SyncContact { FirstName="Test", LastName="Person", Phone="123-123-1234", Email="test#test.com"};
And then with that object I need to create a NameValueCollection where the object's property uses the SyncProperty's PropertyName as the Key in the collection. Then use that to make a post request to an API. So in this case I would end up with a collection like...
collection["first_name"] = "Test"
collection["last_name"] = "Person"
collection["phone"] = "123-123-1234"
collection["email"] = "test#test.com"
How can I do that?
If you want to get some type metadata, you should use Reflection. To read attribute you can use GetCustomAttribute<AttributeType>() extension for MemberInfo.
This extension method builds sync dictionary from type properties decorated with SyncPropertyAttribute (I suggest to use the dictionary instead of NameValueCollection):
public static Dictionary<string, string> ToSyncDictionary<T>(this T value)
{
var syncProperties = from p in typeof(T).GetProperties()
let name = p.GetCustomAttribute<SyncProperty>()?.PropertyName
where name != null
select new {
Name = name,
Value = p.GetValue(value)?.ToString()
};
return syncProperties.ToDictionary(p => p.Name, p => p.Value);
}
Usage:
var collection = contact.ToSyncDictionary();
Output:
{
"first_name": "Test",
"last_name": "Person",
"phone": "123-123-1234",
"email": "test#test.com"
}
Note: if you are going to use contact data in POST request, then you should consider using simple JSON serialization attributes instead of creating your own attributes. E.g. with Json.NET:
public class SyncContact
{
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
[JsonProperty("phone")]
public string Phone { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
}
Then simple serialization will do the job:
string json = JsonConvert.SerializeObject(contact);
And the result will be exactly same as above.
The attributes belong to the properties of the class, so you need to get the type of the class, then find the appropriate properties and then get the custom attributes.
Something like:
var contact = new SyncContact { FirstName="Test", LastName="Person", Phone="123-123-1234", Email="test#test.com"};
var t = typeof(SyncContact);
var props = t.GetProperties().Select(p => new { p, attr = p.GetCustomAttribute<SyncProperty>() }).Where(p => p.attr != null);
var dict = props.ToDictionary(p => p.attr.PropertyName, v => v.p.GetValue(contact) );
This leaves out any properties that aren't tagged, but you can decide to handle those differently if you wanted to.
Fiddle
Assuming SyncProperty is tagged on every property, this should do the job:
var contact = new SyncContact { FirstName="Test", LastName="Person", Phone="123-123-1234", Email="test#test.com"};
var collection = contact.GetType().GetProperties()
.Select(x => new
{
x.GetCustomAttribute<SyncProperty>().PropertyName,
Value = x.GetValue(contact).ToString()
})
.ToDictionary(x => x.PropertyName, x => x.Value);
As a helper method:
public static class SynxHelper
{
public static Dictionary<string, string> Serialize<T>(T obj)
{
return typeof(T).GetProperties()
.Select(x => new
{
SyncProperty = x.GetCustomAttribute<SyncProperty>(),
Value = x.GetValue(obj)
})
.Where(x => x.SyncProperty != null)
.ToDictionary(x => x.SyncProperty.PropertyName, x => x.Value.ToString());
}
}
// usage
var collection = SynxHelper.Serialize(contact);
Here is a one line linq solution for this
(from prop in obj.GetType().GetProperties()
where prop.GetCustomAttribute<SyncProperty>() != null
select new { Key = prop.GetCustomAttribute<SyncProperty>().PropertyName, Value = prop.GetValue(obj) })
.ToDictionary(k => k.Key, v => v.Value);
BUT!!!!!!!! Don't try doing this your self. This is not optimized and slow as all reflection is.
This is just to demonstrate how bad reflection is
static void Main(string[] args)
{
var data = Enumerable.Range(0, 10000).Select(i => new SyncContact { FirstName = "Test", LastName = "Person", Phone = "123-123-1234", Email = "test#test.com" }).ToArray();
Stopwatch sw = new Stopwatch();
long m1Time = 0;
var total1 = 0;
sw.Start();
foreach (var item in data)
{
var a = ToSyncDictionary(item);
total1++;
}
sw.Stop();
m1Time = sw.ElapsedMilliseconds;
sw.Reset();
long m2Time = 0;
var total2 = 0;
sw.Start();
foreach (var item in data)
{
var a = ToSyncDictionary2(item);
total2++;
}
sw.Stop();
m2Time = sw.ElapsedMilliseconds;
Console.WriteLine($"ToSyncDictionary : {m1Time} for {total1}");
Console.WriteLine($"ToSyncDictionary2 : {m2Time} for {total2}");
Console.ReadLine();
}
public static IDictionary<string, string> ToSyncDictionary<T>(T value)
{
var syncProperties = from p in typeof(T).GetProperties()
let name = p.GetCustomAttribute<SyncProperty>()?.PropertyName
where name != null
select new
{
Name = name,
Value = p.GetValue(value)?.ToString()
};
return syncProperties.ToDictionary(p => p.Name, p => p.Value);
}
public static IDictionary<string, string> ToSyncDictionary2<T>(T value)
{
return Mapper<T>.ToSyncDictionary(value);
}
public static class Mapper<T>
{
private static readonly Func<T, IDictionary<string, string>> map;
static Mapper()
{
map = ObjectSerializer();
}
public static IDictionary<string, string> ToSyncDictionary(T value)
{
return map(value);
}
private static Func<T, IDictionary<string, string>> ObjectSerializer()
{
var type = typeof(Dictionary<string, string>);
var param = Expression.Parameter(typeof(T));
var newExp = Expression.New(type);
var addMethod = type.GetMethod(nameof(Dictionary<string, string>.Add), new Type[] { typeof(string), typeof(string) });
var toString = typeof(T).GetMethod(nameof(object.ToString));
var setData = from p in typeof(T).GetProperties()
let name = p.GetCustomAttribute<SyncProperty>()?.PropertyName
where name != null
select Expression.ElementInit(addMethod,
Expression.Constant(name),
Expression.Condition(Expression.Equal(Expression.Property(param, p), Expression.Constant(null)),
Expression.Call(Expression.Property(param, p), toString),
Expression.Constant(null,typeof(string))));
return Expression.Lambda<Func<T, IDictionary<string, string>>>(Expression.ListInit(newExp, setData), param).Compile();
}
}
On my machine I got a 10x boost in pers.
If you can use some serializer like JSON.net since you will need to change a lot of things to make it work well and you already have a tone of stuff that dose it for you.

Generic Dictionary retrieve value

I have the following C# class:
class BatchData
{
public string batchNumber { get; set; }
public string processDate { get; set; }
public int TotalRecords { get; set; }
public int SuccessCount { get; set; }
}
and a dictionary:
Dictionary<int, BatchData> BatchData = new Dictionary<int, BatchData>();
Now, I want to search the whole dictionary to see if the value
x
is held in:
BatchData.batchNumber
eg
for the whole dictionary, if
BatchData.batchNumber = x
I know Dictionary has a method
.contains
But I am not sure how I can apply this.
EDIT:
BatchData.batchNumber = x
Can occur multiple times within the dictionary
You could do this:
BatchData.Values.Any(x=>x.batchNumber == "x");
For example:
Dictionary<int, BatchData> BatchData = new Dictionary<int, BatchData>();
BatchData.Add(1, new BatchData { batchNumber = "x"});
var hasX = BatchData.Values.Any(x=>x.batchNumber == "x"); //true;
A dictionary is a collection of KeyValuePair objects, each of which has a Key property (an int in your case), and a Value property (a BatchData object).
There could be multiple entries with that batch number. If you just want to see if any key contains that number you can use
batchData.Any(kvp => kvp.Value.batchNumber == x);
If you want all key-value pairs with that batch number, change to Where:
batchData.Where(kvp => kvp.Value.batchNumber == x);
You can also use First, Single, etc. as appropriate.
You should use batchNumber as the key to your dictionary:
Dictionary<string, BatchData> BatchData = new Dictionary<string, BatchData>();
BatchValues.Add(batch1.batchNumber, batch1);
BatchValues.Add(batch2.batchNumber, batch2);
BatchValues.Add(batch3.batchNumber, batch3);
...
Then checking for existence is an O(1) operation (link):
BatchValues.ContainsKey(batchNumber);
You can use another one solution by Contains method from System.Linq.
First, you need to implement IEqualityComparer<> interface
public class BatchDataComparer : IEqualityComparer<KeyValuePair<int, BatchData>>
{
public bool Equals(KeyValuePair<int, BatchData> x, KeyValuePair<int, BatchData> y)
{
return (x.Value.batchNumber == y.Value.batchNumber);
}
public int GetHashCode(KeyValuePair<int, BatchData> obj)
{
//or something else what you need
return obj.Value.batchNumber.GetHashCode();
}
}
After that, you can get value from Dictionary like this:
private static void Main(string[] args)
{
Dictionary<int, BatchData> dic = new Dictionary<int, BatchData>();
dic.Add(1, new BatchData() { batchNumber = "x" });
dic.Add(2, new BatchData() { batchNumber = "y" });
dic.Add(3, new BatchData() { batchNumber = "z" });
bool contain = dic.Contains(new KeyValuePair<int, BatchData>(100, new BatchData()
{
batchNumber = "z"
}), new BatchDataComparer());
Console.ReadKey();
}
public class BatchData
{
public string batchNumber { get; set; }
public string processDate { get; set; }
public int TotalRecords { get; set; }
public int SuccessCount { get; set; }
}

How to create a dynamic LINQ select projection function from a string[] of names?

Using C#...
Is there any way to specify property names for a projection function on a LINQ select method, from an array.
public class Album
{
public int Id { get; set; }
public string Name { get; set; }
public short Rate { get; set; }
public string Genre { get; set; }
public short Tracks { get; set; }
}
public class Class1
{
private void Some<T>()
{
// Example of source
var names = new[] { "Id", "Name", "Tracks" };
var query = myDataContext.
GetTable<T>.
AsQueryable().
Select( /* dynamic projection from names array */ );
// something like
// Select(x => new
// {
// x.Id,
// x.Name,
// x.Tracks
// }
GoAndDoSomethingWith(query);
}
}
Could this be done without System.Linq.Dynamic?
You could use reflection and dynamic types to generate an object with only the specified fields/properties.
Below is a simple way of doing this. You can do optimizations, like having a type cache for the reflection. But this should work for simple fields/properties.
public static object DynamicProjection(object input, IEnumerable<string> properties)
{
var type = input.GetType();
dynamic dObject = new ExpandoObject();
var dDict = dObject as IDictionary<string, object>;
foreach (var p in properties)
{
var field = type.GetField(p);
if (field != null)
dDict [p] = field.GetValue(input);
var prop = type.GetProperty(p);
if (prop != null && prop.GetIndexParameters().Length == 0)
dDict[p] = prop.GetValue(input, null);
}
return dObject;
}
Usage:
//...
var names = new[] { "Id", "Name", "Tracks" };
var projection = collection.Select(x => DynamicProjection(x, names));
//...

Categories