I started playing with roslyn few days ago and i am trying to write an extension method which tells if an IPropertySymbol has a backing field, so i thought a property has a backing field if and only if the following does not apply(as far as i am concerned):
IF its Abstract
IF its Extern
IF its ReadOnlyProperty
IF the Getter or the Setter has no Body or Empty body
so i came up with
public static bool HasBackingField(this IPropertySymbol property)
{
return !(property.IsAbstract || property.IsExtern || property.IsReadOnly);
}
My questions are
Did i miss any condition?
How do i check for the last condition? i found GetMethod and SetMethodproperties in IPropertySymbol but i don't know to check if they have a body
example to start up with
var code =
#"class XYZ
{
public int x => 4; //HasBacking field : false IsReadOnly
public int m { get { return 0;}} //HasBacking field : false IsReadOnly
public int y { get; set; } //HasBacking field : false Null body for setter or getter
public int z { get { return 0; } set { } } //HasBacking field : false Empty body for setter or getter
private int _g;
public int g //HasBacking field : true Getter and Setter has no empty Bodies
{
get { return _g; }
set { _g = value; }
}
}";
var syntaxTree = CSharpSyntaxTree.ParseText(code);
var compilation = CSharpCompilation.Create("xxx").AddSyntaxTrees(syntaxTree);
var classSymbol = compilation.GetTypeByMetadataName("XYZ");
var propSymbols = classSymbol.GetMembers().OfType<IPropertySymbol>();
var results = propSymbols.Select(ps => ps.HasBackingField()); //should be [false false false false true]
I decided to look at the syntax representation rather than the actual symbol -- the syntax is at a lower level than the symbol and contains the raw information we're interested in: looking at individual statements.
This seems to do what you're interested in:
internal static bool HasBackingField(this PropertyDeclarationSyntax property)
{
var getter = property.AccessorList?.Accessors.FirstOrDefault(x => x.IsKind(SyntaxKind.GetAccessorDeclaration));
var setter = property.AccessorList?.Accessors.FirstOrDefault(x => x.IsKind(SyntaxKind.SetAccessorDeclaration));
if (setter?.Body == null || getter?.Body == null)
{
return false;
}
bool setterHasBodyStatements = setter.Body.Statements.Any();
bool getterHasBodyStatements = getter.Body.Statements.Any();
return setterHasBodyStatements && getterHasBodyStatements;
}
Note that I'm not convinced this is reliable enough to conclude that there is a backing field available, but it follows the idea you had by checking if there is a body or not.
I haven't added the other checks you had in mind but these can trivially be added (either use the symbol as you already do or look through the PropertyDeclarationSyntax its modifiers/attributes).
---
Full code to test it out yourself:
public static void Execute()
{
var code =
#"class XYZ
{
public int x => 4; //HasBacking field : false IsReadOnly
public int m { get { return 0;}} //HasBacking field : false IsReadOnly
public int y { get; set; } //HasBacking field : false Null body for setter or getter
public int z { get { return 0; } set { } } //HasBacking field : false Empty body for setter or getter
private int _g;
public int g //HasBacking field : true Getter and Setter has no empty Bodies
{
get { return _g; }
set { _g = value; }
}
}";
var tree = CSharpSyntaxTree.ParseText(code);
var root = tree.GetRoot();
foreach (var prop in root.DescendantNodes().OfType<PropertyDeclarationSyntax>())
{
Console.WriteLine(prop.HasBackingField());
}
}
}
internal static class Extensions
{
internal static bool HasBackingField(this PropertyDeclarationSyntax property)
{
var getter = property.AccessorList?.Accessors.FirstOrDefault(x => x.IsKind(SyntaxKind.GetAccessorDeclaration));
var setter = property.AccessorList?.Accessors.FirstOrDefault(x => x.IsKind(SyntaxKind.SetAccessorDeclaration));
if (setter?.Body == null || getter?.Body == null)
{
return false;
}
bool setterHasBodyStatements = setter.Body.Statements.Any();
bool getterHasBodyStatements = getter.Body.Statements.Any();
return setterHasBodyStatements && getterHasBodyStatements;
}
}
Related
I will try to explain my best, but it is a tricky one to explain.
I am having a problem using reflection when a derived object redefines a property already in a base class.
Let's consider the following classes to start with:
// The base class
namespace MyNamesapce
{
public abstract class MyClassBase: IMyClassBase
{
[JsonConstructor]
public MyClassBase()
{
}
public string Info { get; set; }
public string Unit { get; set; }
public string Name { get; set; }
}
}
// MyClassArray is most of the time used
namespace MyNamesapce
{
public class MyClassArray<TType> : MyClassBase, IMyClassArray<TType>
{
public MyClassArray()
{
}
[JsonConstructor]
public MyClassArray(IEnumerable<TType> value, TType minValue, TType maxValue)
{
MinValue = minValue;
MaxValue = maxValue;
Value = value;
}
public IEnumerable<TType> Value { get; set; }
public TType MinValue { get; set; }
public TType MaxValue { get; set; }
}
}
// In some rare cases we need 2D arrays
namespace MyNamesapce
{
public class MyClass2DArray<TType> : MyClassArray<TType>, IMyClass2DArray<TType>
{
private int[] _arraySize { get; set; }
public MyClass2DArray()
{
}
[JsonConstructor]
public MyClass2DArray(TType[][] value, TType minValue, TType maxValue)
{
MinValue = minValue;
MaxValue = maxValue;
_arraySize = new int[2] { value.Count(), value[0].Length };
Value = value;
}
public new TType[][] Value
{
get
{
TType[][] array2D = new TType[_arraySize[0]][];
// Reconstruct the 2D array
TType[] tmpArray;
int startIdx = 0;
for (int numArrays = 0; numArrays < _arraySize[0]; numArrays++)
{
tmpArray = new TType[_arraySize[1]];
Array.Copy(base.Value.ToArray(), startIdx, tmpArray, 0, _arraySize[1]);
startIdx += _arraySize[1];
array2D[numArrays] = tmpArray;
}
return array2D;
}
set
{
// Should not be able to set _value to null
if (value == null)
return;
base.Value = value.SelectMany(v => v).ToArray();
}
}
}
}
I now need to get all the properties from all instances of MyClassArray and MyClassArray2D. You will say, there are plenty of threads discussing that very point, just use "GetType().GetProperties()" for the former and use "GetType().GetProperty(..., BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)" for the latter.
The problem is that I do not know in advance which class is being processed. In my system when deserialising a Json, instances of both MyClassArray and MyClassArray2D have to be reconstructed, which is done using the following setter:
public static void SetProperty(this Object obj, string propName, Object value)
{
PropertyInfo info = null;
object[] indexer = null;
string[] nameParts = propName.Split('.');
if (obj == null) { return; }
var props = obj.GetType().GetProperties();
for (int idx = 0; idx < nameParts.Count() - 1; idx++)
{
try
{
indexer = null;
// Try to access normal property
info = obj.GetType().GetProperty(nameParts[idx]);
if (info == null)
continue;
obj = info.GetValue(obj, indexer);
}
catch
{
info = null;
indexer = null;
}
}
if (obj != null)
{
// !!! Note that here, using declare only will only work when using derived classes
PropertyInfo propertyToSet = obj.GetType().GetProperty(nameParts.Last(), BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); // | BindingFlags.DeclaredOnly);
propertyToSet?.SetValue(obj, value);
}
else
{
throw new SystemException($"Could not find the property {propName}");
}
}
As you can see an object is passed in to SetProperty() (that can be of any type).
When it is of type MyClassArray, there are no problems, but if it is of type MyClassArray2D it does not quite work as the latter redefines "Value", which will break the logic as 2 properties called value will exist. I need a way to detect that.
The first loop seems to do the right thing. "obj = info.GetValue(obj, indexer);" will return "obj" containing all the versions of "Value". The problem is in the next part of SetProperty().
How can I detect when more than one "Value" property is in "obj"? And how to always pick the derived version of "Value"?
Also if I just use "BindingFlags.DeclaredOnly" as done here in my code snipet, properties from the base class get lost/disappear, which is undesirable.
Is there maybe a way to return in "obj" all the properties without the duplicates coming from the base class? Or some kind of property filter maybe?
There are two classes
class A
{
public string ProductionDivision { get; set; }
}
class B
{
private object _productionDivision;
public enum ProductionDivisionOneofCase
{
None = 0,
IsNullproductionDivision = 15,
ProductionDivisionValue = 16,
}
private ProductionDivisionOneofCase _productionDivisionCase = ProductionDivisionOneofCase.None;
public bool IsNullProductionDivision
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.IsNullproductionDivision ? (bool)_productionDivision : false; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.IsNullproductionDivision;
}
}
public string ProductionDivisionValue
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.ProductionDivisionValue ? (string)_productionDivision : ""; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.ProductionDivisionValue;
}
}
}
I would like to map the ProductionDivision property to one of properties of class B depending on the condition - null(map to IsNullProductionDivision) or not null(map to ProductionDivisionValue) of the source property value. I can achieve it like as below.
CreateMap<A, B>()
.ForMember(g => g.IsNullProductionDivision, m =>
{
m.PreCondition(s => s.ProductionDivision == null);
m.MapFrom(b => true);
})
.ForMember(g => g.ProductionDivisionValue, m =>
{
m.PreCondition(s => s.ProductionDivision != null);
m.MapFrom(b => b.ProductionDivision);
});
If the value of {source property name} is null then the value of IsNull{source property name} is true.
Else if the value of {source property name} is not null then the value of {source property name}Value is the value of {source property name}.
I have many properties that respond this mapping rule. So, I don't want to write mapping rule for each properties like above. I want to configurate a rule for such mapping globally.
How can I configure AutoMapper so that it can handle such complex mapping?
I have found solution. The solution is pretty simple and clear. It turns out as follow:
Full code:
public class Program
{
class A
{
public string ProductionDivision { get; set; }
}
class B
{
private object _productionDivision;
public enum ProductionDivisionOneofCase
{
None = 0,
IsNullproductionDivision = 15,
ProductionDivisionValue = 16,
}
private ProductionDivisionOneofCase _productionDivisionCase = ProductionDivisionOneofCase.None;
public bool IsNullProductionDivision
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.IsNullproductionDivision ? (bool)_productionDivision : false; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.IsNullproductionDivision;
}
}
public string ProductionDivisionValue
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.ProductionDivisionValue ? (string)_productionDivision : ""; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.ProductionDivisionValue;
}
}
}
public class StrinToBoolCustomResolver
: IValueConverter<string, bool>
{
public bool Convert(string sourceMember, ResolutionContext context)
{
return sourceMember == null;
}
}
public class MyAutoMapperProfile
: Profile
{
public MyAutoMapperProfile()
{
// add post and pre prefixes to add corresponding properties in the inner property map
RecognizeDestinationPostfixes("Value");
RecognizeDestinationPrefixes("IsNull");
// add mapping for "value" property
this.ForAllPropertyMaps(map => map.SourceMember.Name + "Value" == map.DestinationName,
(map, expression) =>
{
expression.Condition((source, dest, sMem, destMem) =>
{
return sMem != null;
});
expression.MapFrom(map.SourceMember.Name);
});
// add mapping for "IsNull" property
this.ForAllPropertyMaps(map => "IsNull" + map.SourceMember.Name == map.DestinationName,
(map, expression) =>
{
expression.Condition((source, dest, sMem, destMem) =>
{
return (bool)sMem;
});
//expression.MapFrom(map.SourceMember.Name);
expression.ConvertUsing<string, bool>(new StrinToBoolCustomResolver(), map.SourceMember.Name);
});
CreateMap<A, B>();
//.ForMember(g => g.IsNullProductionDivision, m =>
//{
// m.PreCondition(s => s.ProductionDivision == null);
// m.MapFrom(b => true);
//});
//.ForMember(g => g.ProductionDivisionValue, m =>
//{
// m.PreCondition(s => s.ProductionDivision != null);
// m.MapFrom(b => b.ProductionDivision);
//});
}
}
public static void Main(string[] args)
{
var configuration = new MapperConfiguration(cfg => {
cfg.AddProfile(new MyAutoMapperProfile());
});
var mapper = new Mapper(configuration);
mapper.ConfigurationProvider.AssertConfigurationIsValid();
var a = new A()
{
ProductionDivision = null
};
// b._productionDivisionCase will be equal IsNullproductionDivision
var b = mapper.Map<B>(a);
var c = new A()
{
ProductionDivision = "dsd"
};
// d._productionDivisionCase will be equal ProductionDivisionValue
var d = mapper.Map<B>(c);
}
}
Clarification:
add (post/pre)fixes to add corresponding properties to inner property map. It need to do here because our properties should be catched by AutoMapper. Otherwise properties will be abandoned and mapper configuration will be failed.
After that, we configurate how these properties need to be mapping. We call ForAllPropertyMaps method, filtering all properties and setting a rule to mapping properties suitable with our filter. When the mapper object is creating, the execution plan will be built taking look the specified filters.
For "Value" property we add a condition to check whether the source property is not null. If it is null, then mapping will be missed.
For "IsNull" property First of all, we add a converter to convert string type to bool type. The converter just compares the source string property with null value. If the source property equals null, then the converter returns true. So, the condition receives a true value, returns true, and mapping will be done. Otherwise the converter returns false, so the condition returns false and mapping will be missed.
Thus, the mapping of source property will occur to different destination properties depending on whether the source property is null value. Besides that, corresponding set methods of corresponding destination properties will be not called if it not must to be called.
What is the correct way to check that a given property info is an implementation of a property from an interface?
There is a class InterfaceMap that solve this problem for methods. But for properties, it provides two separate mappings for getter and setter and there still remains a problem to match those with the corresponding interface methods.
public interface IA
{
int X { get; set; }
}
public interface IB
{
int X { get; set; }
}
public class C : IA, IB
{
public int X { get; set; }
int IB.X { get; set; }
}
public PropertyInfo GetProperty<TClass, TProperty>(Expression<Func<TClass, TProperty>> getProperty)
{
return (PropertyInfo)((MemberExpression)getProperty.Body).Member;
}
[Test]
public void Check()
{
var aProperty = GetProperty((IA x) => x.X);
var bProperty = GetProperty((IB x) => x.X);
var cPropertyA = GetProperty((C x) => x.X);
var cPropertyB = GetProperty((C x) => ((IB)x).X);
CompareProperties(cPropertyA, aProperty); // True
CompareProperties(cPropertyA, bProperty); // False
CompareProperties(cPropertyB, aProperty); // False
CompareProperties(cPropertyB, bProperty); // True
}
private bool CompareProperties(PropertyInfo classProperty, PropertyInfo interfaceProperty)
{
// TODO implement
}
From a given PropertyInfo, you can use the GetMethod and SetMethod properties to access the MethodInfo of the getter and setter, respectively.
Thus, it should be possible to compare those in a little helper method:
private static bool MethodsImplements(InterfaceMap interfaceMap,
MethodInfo interfaceMethod, MethodInfo classMethod)
{
var implIndex = Array.IndexOf(interfaceMap.InterfaceMethods, interfaceMethod);
return interfaceMethod == interfaceMap.TargetMethods[implIndex];
}
This can then be used as follows to fulfil your desired method:
var interfaceType = interfaceProperty.DeclaringType;
var interfaceMap = classProperty.DeclaringType.GetInterfaceMap(interfaceType);
var gettersMatch = classProperty.CanRead && interfaceProperty.CanRead
&& MethodImplements(interfaceMap, interfaceProperty.GetMethod, classProperty.GetMethod);
var settersMatch = classProperty.CanWrite && interfaceProperty.CanWrite
&& MethodImplements(interfaceMap, interfaceProperty.SetMethod, classProperty.SetMethod);
Then, return gettersMatch || settersMatch, as the interface property may have only a getter or only a setter.
I'm posting this question to find a simpler way of achieving a result.
We have a big IF statement that checks for NULL or string.empty. Something like this:
if (string.IsNullOrEmpty(Empl.Name) || string.IsNullOrEmpty(Empl.last) ||
string.IsNullOrEmpty(Empl.init) || string.IsNullOrEmpty(Empl.cat1) ||
string.IsNullOrEmpty(Empl.history) || string.IsNullOrEmpty(Empl.cat2) ||
string.IsNullOrEmpty(Empl.year) || string.IsNullOrEmpty(Empl.month) ||
string.IsNullOrEmpty(Empl.retire) || string.IsNullOrEmpty(Empl.spouse) ||
string.IsNullOrEmpty(Empl.children) || string.IsNullOrEmpty(Empl.bday) ||
string.IsNullOrEmpty(Empl.hire)|| string.IsNullOrEmpty(Empl.death) ||
string.IsNullOrEmpty(Empl.JobName) || string.IsNullOrEmpty(Empl.More) ||
string.IsNullOrEmpty(Empl.AndMore))
{
//Display message. Something like "Error: Name and Month is missing"
return;
}
Any solution I've found so far to address this is time-consuming, and would require writing more code.
Is there any way to know which value is string.IsNullOrEmpty without having to change this IF statement too much? Worse-case, I can check every single statement separately, but I would prefer not doing this.
Thanks.
No, there's no "magic" function that will tell you which of a series of expression in an OR statement are true. Also, since you're using the short-circuiting version, the statement will return true after the first true condition, so the remaining expressions are not even evaluated.
However, you could do something like this:
bool[] checks = {
string.IsNullOrEmpty(Empl.Name) , string.IsNullOrEmpty(Empl.last) ,
string.IsNullOrEmpty(Empl.init) , string.IsNullOrEmpty(Empl.cat1) ,
string.IsNullOrEmpty(Empl.history) , string.IsNullOrEmpty(Empl.cat2) ,
string.IsNullOrEmpty(Empl.year) , string.IsNullOrEmpty(Empl.month) ,
string.IsNullOrEmpty(Empl.retire) , string.IsNullOrEmpty(Empl.spouse) ,
string.IsNullOrEmpty(Empl.children) , string.IsNullOrEmpty(Empl.bday) ,
string.IsNullOrEmpty(Empl.hire) , string.IsNullOrEmpty(Empl.death) ,
string.IsNullOrEmpty(Empl.JobName) , string.IsNullOrEmpty(Empl.More) ,
string.IsNullOrEmpty(Empl.AndMore)
};
if(checks.Any())
{
//Display message. Something like "Error: Name and Month is missing"
return;
}
now the checks variable holds the result of each expression.
I find this sort of an more elegant way to use ModelState.isValid.
Some reference: What is ModelState.IsValid valid for in ASP.NET MVC in NerdDinner?
For your model, you can add following annotation:
[Required(AllowEmptyStrings= false)]
public string Boo { get; set; }
When you do validation, try:
if (!ModelState.IsValid)
{
//Display message. Something like "Error: Name and Month is missing"
return;
}
Yes, write your own string extension method that does the same check, but also takes in a List and add the field name to the list. Declare the list of strings before the if and you will have a list of offending fields where your comment is.
This can be improved upon with a bit of reflection to automatically get the name and maybe make a few optimizations but it is on the right track.
Keep in mind that the first condition that violates the if statement will cause it to fail, so you will get an incomplete list (of one item) unless your if is constructed differently.
public static class StringExtensions
{
public static bool CheckIsNullOrEmptyAndListIt(this string field, string fieldName, List<string> naughties)
{
var result = String.IsNullOrEmpty(field);
if (result == true)
{
naughties.Add(fieldName);
}
return result;
}
}
}
using System.IO;
using System;
using System.Linq;
public class Program
{
public class Dog
{
public static string Name {get;set;}
public static string Race {get;set;}
}
public static bool validate(Dog dog)
{
bool val = true;
var y = dog.GetType()
.GetProperties()
.Select(p =>
{
object value =p.GetValue(dog,null);
if(string.IsNullOrEmpty(value.ToString())){ val=false; return false;}
else return true;
})
.ToArray();
return val;
}
public static void Main()
{
Dog dog= new Dog();
Dog.Name = "Peter";
Dog.Race = "";
if(validate(dog))
{
Console.WriteLine("Hello, World!");
}
}
}
You can use something like this :
public static class ValidationHelper
{
public static IEnumerable<string> FindEmptyProperties<T>(T target, params Expression<Func<T, string>>[] propertySelectors)
{
foreach (var propertySelector in propertySelectors)
{
if (string.IsNullOrEmpty(propertySelector.Compile()(target)))
{
var memberExpr = propertySelector.Body as MemberExpression;
yield return memberExpr.Member.Name;
}
}
}
}
Usage :
var failed = ValidationHelper.FindEmptyProperties(Empl, x => x.Name, x => x.last, x => x.init, x => x.cat1).ToList();
if (failed.Any())
{
throw new InvalidOperationException(
string.Format("Error: {0} is missing",
string.Join(", ", failed)));
}
If you use ASP.NET MVC maybe use DataAnnotations...
For the general c# context consider PostSharp aspect oriented library! Geat project!
Otherwise: Maybe a reflection solution using plain .NET ? (Created just for you! I think i keep for some own projects maybe)
Works with different types and you can control the targeted bindingflags.
Provides a common base class for your data transfer objects. (dto)
Reflection is performance optimized and working for generics as well!
public class Program
{
public void Main()
{
Empl test = new Empl()
{
TestProp = "blub",
TestInt = 1
};
if (test.ValidateProperties(Validations.CheckEmptyStringsAndZeroInts))
{
Console.WriteLine("validation passed");
}
else
{
Console.WriteLine("validation failed");
}
}
}
private static class Validations
{
//put this in a static class with standard checks
public static Func<object, bool> CheckEmptyStringsAndZeroInts = o =>
{
if (o is string && string.IsNullOrEmpty((string)o))
{
return false;
}
else if (o is int && ((int) o) == 0)
{
return false;
}
// ignore other property types
return true;
};
}
// Derive all your models like this. deriving from an Empl class is still valid and working!
//[IncludeBindingFlagsForPropertyReflctionAttribute(/*your custom binding flags*/)] //can also override the binding flags in derived classes!
public class Empl : DtoBase<Empl>
{
public string TestProp { get; set; }
public int TestInt { get; set; }
// Your properties here
}
// Helps you to control the targeted properties. you can filter for public or protected members for example
public class IncludeBindingFlagsForPropertyReflctionAttribute : Attribute
{
public BindingFlags BindingFlags { get; }
public IncludeBindingFlagsForPropertyReflctionAttribute(BindingFlags propertySearchBindingFlags)
{
BindingFlags = propertySearchBindingFlags;
}
}
//Looks much. But used once as base class can do those validations for you
[IncludeBindingFlagsForPropertyReflction(BindingFlags.Public | BindingFlags.Instance)]
public abstract class DtoBase<TDto> where TDto : DtoBase<TDto>
{
private static Dictionary<Type, List<PropertyInfo>> DtoPropertyInfosStorage { get; }
private List<PropertyInfo> DtoPropertyInfos => DtoPropertyInfosStorage[typeof (TDto)];
static DtoBase()
{
DtoPropertyInfosStorage = new Dictionary<Type, List<PropertyInfo>>();
Type tDto = typeof (TDto);
var includeBindingFlagsForProperty = GetAttribute(tDto);
BindingFlags defaultTargetFlags = BindingFlags.Instance | BindingFlags.Public;
DtoPropertyInfosStorage.Add(typeof(TDto), new List<PropertyInfo>(typeof(TDto).GetProperties(includeBindingFlagsForProperty?.BindingFlags ?? defaultTargetFlags)));
}
private static IncludeBindingFlagsForPropertyReflctionAttribute GetAttribute(Type dtoType)
{
bool stopRecursion = !dtoType.IsSubclassOf(typeof(DtoBase<TDto>));
var includeBindingFlagsForProperty = dtoType.GetCustomAttributes(typeof(IncludeBindingFlagsForPropertyReflctionAttribute)).FirstOrDefault();
if (includeBindingFlagsForProperty == null && !stopRecursion)
{
return GetAttribute(dtoType.BaseType);
}
return null;
}
/// <summary>
/// You can handle your validation type in you validation function yourself.
/// </summary>
public bool ValidateProperties(Func<object, bool> validationFunction)
{
foreach (KeyValuePair<Type, List<PropertyInfo>> dtoPropertyInfo in DtoPropertyInfosStorage)
{
foreach (PropertyInfo propertyInfo in DtoPropertyInfos)
{
if (!validationFunction(propertyInfo.))
{
return false;
}
}
}
return true;
}
/// <summary>
/// You can pass your targeted property type like string to TPropertyType
/// <![CDATA[ Example:
/// if(ValidateProperties<string>(validate => !string.IsNullOrEmpty(validate)))
/// {
/// properties not empty?
/// }
/// ]]]]>
/// </summary>
public bool ValidateProperties<TPropertyType>(Func<TPropertyType, bool> validationFunction)
{
List<PropertyInfo> targetPropertyInfos =
DtoPropertyInfos.Where(prop => prop.PropertyType == typeof (TPropertyType))
.ToList();
foreach (PropertyInfo dtoPropertyInfo in targetPropertyInfos)
{
if (validationFunction((TPropertyType) dtoPropertyInfo.GetValue(this)))
{
return false;
}
}
return true;
}
}
I have two ObservableCollection in my application, one which contain the items of type StoreSettings
named "oldSettings" anothe collection named "_stores".
public ObservableCollection<StoreSettings> oldSettings;
private ObservableCollection<StoreSettings> _stores;
Here is my StoreSettings class
public class StoreSettings :INotifyPropertyChanged
{
private bool _autoAOD;
public bool AutoAOD
{
get { return _autoAOD; }
set { _autoAOD = value;
}
private bool _autoGRN;
public bool AutoGRN
{
get { return _autoGRN; }
set { _autoGRN = value;
}
private bool _directPurchase;
public bool DirectPurchase
{
get { return _directPurchase; }
set { _directPurchase = value;
}
private decimal _gustoreID;
public decimal GUStoreID
{
get { return _gustoreID; }
set { _gustoreID = value;
}
private string _storeCode;
public string Storecode
{
get { return _storeCode; }
set { _storeCode = value;
}
I am updating some of the item's properties through my application, how can i find the modified items
through linq?
this is what i have tried, but it always gives count "0"
List<StoreSettings> result = _vmStoreconfig.oldSettings.Except(_vmStoreconfig.Stores).ToList();
If these two lists contain the same object instances, then it won't work because changes to object properties in one list will be applied to the other list also (since they are the same instances).
This means that you have to either:
clone objects before changing them, or
create a new instance on each change.
If they are not the same instances (i.e. if they are cloned or came through a database roundrip), then you need to provide a way for the runtime to compare individual properties.
You can either override the Equals method of the StoreSettings, or use a custom equality comparer for your StoreSettings class.
Something like:
public class StoreSettingsEqualityComparer : IEqualityComparer<StoreSettings>
{
public bool Equals(StoreSettings x, StoreSettings y)
{
if (object.ReferenceEquals(x, null))
return object.ReferenceEquals(y, null);
return
x.AutoAOD == y.AutoAOD &&
x.AutoGRN == y.AutoGRN &&
...
}
public int GetHashCode(StoreSettings obj)
{
unchecked
{
var h = 31;
h = h * 7 + obj.AutoAOD.GetHashCode();
...
return h;
}
}
}
And then use an overload of Enumerable.Except which accepts a custom comparer:
var comparer = new StoreSettingsEqualityComparer();
var results = first.Except(second, comparer).ToList();
This should get you the changes
List<StoreSettings> changes = _vmStoreconfig.oldSettings.FindAll(delegate(StoreSettings item1)
{
StoreSettings found = _vmStoreconfig.Stores.Find(delegate(StoreSettings item2) {
// Specify comparisons between properties here
return item2.propertyA == item1.propertyA ...;
}
return found != null;
});