Iterating over class properties using LINQ - c#

There is a ParsedTemplate class that it has over 300 property (typed Details and BlockDetails). The parsedTemplate object will be fill by a function. After filling this object I need a LINQ (or other way) to find is there any property like "body" or "img" where IsExist=false and Priority="high".
public class Details
{
public bool IsExist { get; set; }
public string Priority { get; set; }
}
public class BlockDetails : Details
{
public string Block { get; set; }
}
public class ParsedTemplate
{
public BlockDetails body { get; set; }
public BlockDetails a { get; set; }
public Details img { get; set; }
...
}

You're going to need to write your own method to make this appetizing. Fortunately, it doesn't need to be long. Something like:
static IEnumerable<Details> GetDetails(ParsedTemplate parsedTemplate)
{
return from p in typeof(ParsedTemplate).GetProperties()
where typeof(Details).IsAssignableFrom(p.PropertyType)
select (Details)p.GetValue(parsedTemplate, null);
}
You could then, if you wanted to check if any property "exists" on a ParsedTemplate object, for example, use LINQ:
var existingDetails = from d in GetDetails(parsedTemplate)
where d.IsExist
select d;

If you really wanted to use linq while doing that, you could try something like that:
bool isMatching = (from prop in typeof(ParsedTemplate).GetProperties()
where typeof(Details).IsAssignableFrom(prop.PropertyType)
let val = (Details)prop.GetValue(parsedTemplate, null)
where val != null && !val.IsExist && val.Priority == "high"
select val).Any();
Works on my machine.
Or in extension method syntax:
isMatching = typeof(ParsedTemplate).GetProperties()
.Where(prop => typeof(Details).IsAssignableFrom(prop.PropertyType))
.Select(prop => (Details)prop.GetValue(parsedTemplate, null))
.Where(val => val != null && !val.IsExist && val.Priority == "high")
.Any();

Use c# reflection. For example:
ParsedTemplate obj;
PropertyInfo pi = obj.GetType().GetProperty("img");
Details value = (Details)(pi.GetValue(obj, null));
if(value.IsExist)
{
//Do something
}
I haven't compile but i think it work.

ParsedTemplate tpl = null;
// tpl initialization
typeof(ParsedTemplate).GetProperties()
.Where(p => new [] { "name", "img" }.Contains(p.Name))
.Where(p =>
{
Details d = (Details)p.GetValue(tpl, null) as Details;
return d != null && !d.IsExist && d.Priority == "high"
});

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.

Linq Lambda Expression to check null or empty

Im new to Linq and Lambda expressions and I wanted to change this piece of code:
if (string.IsNullOrEmpty(model.PictureUrl1) && string.IsNullOrEmpty(model.Picture2Url) &&
string.IsNullOrEmpty(model.Picture3Url) && string.IsNullOrEmpty(model.Picture4Url) &&
string.IsNullOrEmpty(model.Picture5Url) && string.IsNullOrEmpty(model.Picture6Url))
return Content("");
Now I refactored that model to have an object with those properties and a list of said objects:
public class PublicInfoModelItem
{
public string PictureUrl { get; set; }
public string Text { get; set; }
public string Link { get; set; }
}
And the list:
public List<PublicInfoModelItem> ListOfImages = new List<PublicInfoModelItem>();
Model:
public class PublicInfoModel
{
public List<PublicInfoModelItem> ListOfImages = new List<PublicInfoModelItem>();
public string Container { get; set; }
public int TemplateId { get; set; }
public PublicInfoModelItem InfoModelItem { get; set; }
}
And I was thinking a Lambda expression, something like:
var result = model.ListOfImages.Where(x => string.IsNullOrEmpty(x.PictureUrl)).ToList();
if (result == null)
{
return Content("");
}
I dont know if the above expression is correct or it could be improved.
I think you want to use Any:
if (!model.ListOfImages.Any(x => string.IsNullOrEmpty(x.PictureUrl)))
{
return Content("");
}
Just to append Brett`s solution:
if (!model.ListOfImages.Any(x => string.IsNullOrEmpty(x.PictureUrl)))
{
return Content("");
}
Might be what you were looking for. But it is almost the same as:
if(model.ListOfImages.All(x=> !string.IsNullOrEmpty(x.PictureUrl)))
{
return Content("");
}
the only difference here is: Any will return on the first item that gets evaluated to true and All on the first which gets to false for the predicate. But since All's predicate gets negated, it will occur at the same element in the list and will have the same result.
But All comes in handy when you want some more checking, for example:
if(model.ListOfImages.All(x=> !string.IsNullOrEmpty(x.PictureUrl) && x.PictureUrl.EndsWith(".jpg")))
{
return Content("");
}
You want to find if a null/empty value exists. So another option would be to use Exists():
if(model.ListOfImages.Exists(image => string.IsNullOrEmpty(x.PictureUrl)))
{
return Content("");
}
Yes you can do that. I apply as follows:
var accionistas = repositorio.FindEntitySet<TBL_Accionista>(p => p.Estatus.Equals(1)).Select(a => new { a.Id_Accionista, Nombre = a.Apellido_Paterno + " " + a.Apellido_Materno + " " + a.Primer_Nombre, RazonSocial = String.IsNullOrEmpty(a.Razon_Social)?string.Empty:a.Razon_Social, a.RFC });
You can try something like this to filter out some value:
var result = ListOfImages.Any(x => (!string.IsNullOrEmpty(x.PictureUrl)) && (x.PictureUrl.Contains("text-to-filter")));

How can I filter a collection so I get only the rows where the value of a fields is not null?

I have a call to GetData() that returns a collection of TQ:
IList<TQ> tq = _questionService.GetData();
public class TQ
{
// Index
public int i { get; set; }
// Text
public string text { get; set; }
}
How can I filter what's in tq and make another list where text is not null ?
_questionService.GetData().Where(x => x.text != null);
You might be interested in reading up on LINQ, it will be one of your most important tools in C# development.
If you can use LINQ you can use
var newList = tq.Where(p => p.text != null).ToList()
else something like that
var newList = new List<TQ>();
foreach(var element in tq) {
if (element.text != null) {
newList.Add(element);
}
}

Anonymous Delegate - Search property with collection of objects

Here is the current code in my application using an anonymous delegate to search a collection on properties:
public class MyObject
{
public MyObject() { }
public string MyObjectId { get; set; }
public List<MySubObject> SubObjects { get; set; }
}
public class MySubObject
{
public MySubObject() { }
public string MySubObjectId { get; set; }
}
public List<MyObject> Search(string input)
{
List<MyObject> AllObjects = Data.GetAllObjects();
List<MyObject> SearchResults = new List<MyObject>();
SearchResults = AllObjects.FindAll
(
delegate(MyObject o)
{
return e.MyObjectId.Equals(input)
}
);
if (SearchResults .Count.Equals(0))
SearchResults = null;
return SearchResults ;
}
I want to modify the anonymous delegate to search by the MySubObject.MySubObjectId property in the generic list instead of the MyObjectId property. How would I modify the code in the anonymous delegate to accomplish this?
Try the following
delegate(MyObject o) {
var mySubObject = o as MySubObject;
return mySubObject != null && mySubObject.MySubObjectId == input;
}
Note that you could also use shorter lambda syntax here
(o) => {
var mySubObject = o as MySubObject;
return mySubObject != null && mySubObject.MySubObjectId == input;
}
Or a LINQ query
var searchResult = AllObjects
.OfType<MySubObject>()
.Where(x => x.MySubObjectId == input)
.Cast<MyObject>()
.ToList()
Try this, using lambda expressions. Basically an object is found if at least one of its subobjects contains the required input.
SearchResults = AllObjects.Where(obj =>
obj.SubObjects != null
&& obj.SubObjects.Any(subObj => ubObj.MySubObjectId.Equals(input))
).ToList();
This looks like it could be trimmed down a lot, but you need to get rid of null assignments to the lists:
public List<MyObject> Search(string input)
{
return Data.GetAllObjects()
.Where(obj => obj.SubObjects
.Any(subobj => subobj.SubOjectId.Equals(input)));
}
.Clear() your lists instead of nullifying them, for a more consistent design and a lot less null-checking.

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