C#, filter custom attribute property values - c#

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;
}

Related

What is a more effective and efficient way to check for matching attributes and fields

I have the following model that has these fields:
[Key]
public string Id { get; set; }
[IsSearchable]
public string Code{ get; set; }
[IsSearchable]
public string Name { get; set; }
[IsSearchable]
public string Address { get; set; }
[IsSearchable]
public string PostCode { get; set; }
[IsFilterable]
public int? Setting{ get; set; }
[IsFilterable, IsSortable]
public Location Location { get; set; }
I am writing a method to compare whether values in a database match this model. So far it looks like this:
private bool CompareEquality(Index resultBody, Type indexType)
{
var properties = indexType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
List<PropertyInfo> searchableProperties = new List<PropertyInfo>();
List<PropertyInfo> filterableProperties = new List<PropertyInfo>();
List<PropertyInfo> sortableProperties = new List<PropertyInfo>();
if (properties.Count() == resultBody.Fields.Count)
{
foreach (var property in properties)
{
var isSearchableAttribute = property.GetCustomAttribute<IsSearchableAttribute>();
var isFilterableAttribute = property.GetCustomAttribute<IsFilterableAttribute>();
var isSortableAttribute = property.GetCustomAttribute<IsSortableAttribute>();
if (isSearchableAttribute != null)
{
searchableProperties.Add(property);
}
if (isFilterableAttribute != null)
{
filterableProperties.Add(property);
}
if (isSortableAttribute != null)
{
sortableProperties.Add(property);
}
}
CheckAttributeEquality(searchableProperties, filterableProperties, sortableProperties);
}
return false;
}
The CheckAttributeEquality method:
private bool CheckAttributeEquality(List<PropertyInfo> searchableProperties, List<PropertyInfo> filterableProperties, List<PropertyInfo> sortableProperties)
{
if (searchableProperties.Count == 4 && filterableProperties.Count == 2 && sortableProperties.Count == 1)
{
CheckPropertyFields(searchableProperties, filterableProperties, sortableProperties);
return true;
}
return false;
}
As I started to write a method to check that the field names match, like so:
foreach (var property in searchableProperties)
{
if (property.Name == "Id" ||)
{
...
}
if (property.Name == "Code")
{
...
}
// etc
I realised how messy and long-winded this whole approach is. I am not hugely experienced in C# and would appreciate any advice as to how I can refactor this up a little bit? I want to check for attribute and name matches.
you could use the Typedescriptor (using System.ComponentModel) for that. Try this:
var pdc = TypeDescriptor.GetProperties( this ); //or your model object, if its not "this"
foreach (var property in searchableProperties)
{
var descriptors = pdc[ property.Name ];
// check if your searchable descriptor is there, and do error handling
}
Once it works, you could also try to solve it with LINQ.

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

Create a dictionary of a model?

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

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));
//...

How can I deserialize an XML snippet to configure an already existing object?

Here's my scenario, I've got the following class, and I want to have the constructor deserialize some elements of the class. I would really rather NOT use a factory method here.
public abstract class AccessLevelAgentBase : IAccessLevelAgent
{
public List<AccessLevel> AccessLevels { get; set; }
[XmlElement]
public string PasswordPrompt { get; set; }
[XmlElement]
public string GetAccessLevelKeystroke { get; set; }
[XmlElement]
public int Doohicky { get; set;}
public AccessLevelAgentBase(XElement agentElement)
{
// Some Mojo Here to take agentElement and serialize
// from the XML below to set the values of PasswordPrompt,
// GetAccessLevelKeystroke, and Doohicky.
}
}
The XML:
<AccessLevelAgent>
<PasswordPrompt> Password ?: </PasswordPrompt>
<PromptCommand>Ctrl+X</PromptCommand>
<Doohicky>50</Doohicky>
</AccessLevelAgent>
simple way...
public AccessLevelAgentBase(XElement agentElement)
{
this.AccessLevels = (string)agentElement.Element("AccessLevels");
this.GetAccessLevelKeystroke = (string)agentElement.Element("GetAccessLevelKeystroke");
this.Doohicky = (int)agentElement.Element("Doohicky");
}
... not so simple way...
public AccessLevelAgentBase(XElement agentElement)
{
var type = this.GetType();
var props = from prop in type.GetProperties()
let attrib = prop.GetCustomAttributes(typeof(XmlElementAttribute), true)
.OfType<XmlElementAttribute>()
.FirstOrDefault()
where attrib != null
let elementName = string.IsNullOrWhiteSpace(attrib.ElementName)
? prop.Name
: attrib.ElementName
let value = agentElement.Element(elementName)
where value != null
select new
{
Property = prop,
Element = value,
};
foreach (var item in props)
{
var propType = item.Property.PropertyType;
if (propType == typeof(string))
item.Property.SetValue(this, (string)item.Element, null);
else if (propType == typeof(int))
item.Property.SetValue(this, (int)item.Element, null);
else
throw new NotSupportedException();
}
}

Categories