If you're using the Client Object Model from SharePoint and access properties which haven't been initialized or already retrieved by an
Context.Load(property);
Context.ExecuteQuery();
you get for example a:
Microsoft.SharePoint.Client.PropertyOrFieldNotInitializedException
or
The collection has not been initialized. It has not been requests or
the request has not been executed.
Exception.
Is there any proper way to check before accessing these properties if they are already initialized/retrieved? Without a Try/Catch approach. I don`t like that one's.
I want to check before a Exception has been thrown and handle it.
I already checked the
IsObjectPropertyInstantiated
IsPropertyAvailable
Methods but they don't help really. IsPropertyAvaiable only checks scalar properties and won't give a result on for example Web.Lists and IsObjectPropertyInstantiated returns true for Web.Lists although Web.Lists was not initialized.
I would say your question is already contains the correct answer to some extent.
In order to determine whether client object property is loaded or not the following methods are available:
ClientObject.IsPropertyAvailable method method indicates whether
the specified scalar property has been retrieved or set
ClientObject.IsObjectPropertyInstantiated method indicates
whether the specified property of the client object is instantiated
Tests
Test case 1: load scalar property only
ctx.Load(ctx.Web, w => w.Title);
ctx.ExecuteQuery();
//Results:
ctx.Web.IsObjectPropertyInstantiated("Lists") False
ctx.Web.IsPropertyAvailable("Title") True
Test case 2: load composite property only
ctx.Load(ctx.Web, w => w.Lists);
ctx.ExecuteQuery();
//Results:
ctx.Web.IsObjectPropertyInstantiated("Lists") True
ctx.Web.IsPropertyAvailable("Title") False
Test case 3: load both scalar and composite properties
ctx.Load(ctx.Web, w=>w.Lists,w=>w.Title);
ctx.ExecuteQuery();
//Results
ctx.Web.IsObjectPropertyInstantiated("Lists") True
ctx.Web.IsPropertyAvailable("Title") True
How to dynamically determine whether client object property is loaded or not?
Since ClientObject.IsPropertyAvailable and ClientObject.IsObjectPropertyInstantiated methods expect the property name to be specified as a string value and that could lead to typos, I usually prefer the following extension method:
public static class ClientObjectExtensions
{
/// <summary>
/// Determines whether Client Object property is loaded
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="clientObject"></param>
/// <param name="property"></param>
/// <returns></returns>
public static bool IsPropertyAvailableOrInstantiated<T>(this T clientObject, Expression<Func<T, object>> property)
where T : ClientObject
{
var expression = (MemberExpression)property.Body;
var propName = expression.Member.Name;
var isCollection = typeof(ClientObjectCollection).IsAssignableFrom(property.Body.Type);
return isCollection ? clientObject.IsObjectPropertyInstantiated(propName) : clientObject.IsPropertyAvailable(propName);
}
}
Usage
using (var ctx = new ClientContext(webUri))
{
ctx.Load(ctx.Web, w => w.Lists, w => w.Title);
ctx.ExecuteQuery();
if (ctx.Web.IsPropertyAvailableOrInstantiated(w => w.Title))
{
//...
}
if (ctx.Web.IsPropertyAvailableOrInstantiated(w => w.Lists))
{
//...
}
}
The tests provided by Vadim Gremyachev only cover one half of the scenarios - where you use ctx.Load. But when you use ctx.LoadQuery the result changes:
var query = from lst in ctx.Web.Lists where lst.Title == "SomeList" select lst;
var lists = ctx.LoadQuery(query);
ctx.ExecuteQuery();
ctx.Web.IsObjectPropertyInstantiated("Lists") -> True
ctx.Web.Lists.ServerObjectIsNull -> False
ctx.Web.Lists.Count -> CollectionNotInitializedException
So once the LoadQuery has been called on a collection, you can no longer see if the collection is actually available.
Only way in this case is to detect that the exception occurs.
OK, this is getting more and more complicated especially with SharePoint Online where results of Load and Execute methods may be incomplete even without thrown exceptions. However, below is what I collected from this and other threads combined to a LoadAndExecute method that can be a subclass extension to the ClientContext class or be converted to a static extension class. For a new client object, the object and its properties are loaded in one operation but the result is checked separately for each property. For an existing client object, only missing properties are loaded in separate operations which may consume network resources unnecessarily. So the method doesn't only check which properties are not initialized but tries to retrieve the missing ones, too. In addition, there is another topic of avoiding being throttled by overriding the Execute method of the ClientContext, but that is not included here:
/// <summary>
/// An extended ClientContext to avoid getting throttled.
/// </summary>
public partial class OnlineContext : ClientContext
{
/// <inheritdoc />
public OnlineContext(string webFullUrl, int retryCount = 0, int delay = 0)
: base(webFullUrl)
{
RetryCount = retryCount;
Delay = delay;
}
/// <summary>
/// The retry count.
/// </summary>
public int RetryCount { get; set; }
/// <summary>
/// The delay between attempts in seconds.
/// </summary>
public int Delay { get; set; }
/// <summary>
/// Loads and executes the specified client object properties.
/// </summary>
/// <typeparam name="T">the object type.</typeparam>
/// <param name="clientObject">the object.</param>
/// <param name="properties">the properties.</param>
/// <returns>true if all available, false otherwise.</returns>
public bool LoadAndExecute<T>(T clientObject, params Expression<Func<T, object>>[] properties)
where T : ClientObject
{
int retryAttempts = 0;
int backoffInterval = Math.Max(Delay, 1);
bool retry;
bool available;
do
{
if (clientObject is ClientObjectCollection)
{
// Note that Server Object can be null for collections!
ClientObjectCollection coc = (ClientObjectCollection) (ClientObject) clientObject;
if (!coc.ServerObjectIsNull.HasValue || !coc.ServerObjectIsNull.Value)
{
available = coc.AreItemsAvailable;
}
else
{
available = false;
break;
}
}
else if (clientObject.ServerObjectIsNull.HasValue)
{
available = !clientObject.ServerObjectIsNull.Value;
break;
}
else
{
available = false;
}
if (!available && retryAttempts++ <= RetryCount)
{
if (retryAttempts > 1)
{
Thread.Sleep(backoffInterval * 1000);
backoffInterval *= 2;
}
Load(clientObject, properties);
ExecuteQuery();
retry = true;
}
else
{
retry = false;
}
} while (retry);
if (available)
{
if (properties != null && properties.Length > 0)
{
foreach (Expression<Func<T, object>> property in properties)
{
if (!LoadAndExecuteProperty(clientObject, property, retryAttempts > 0))
{
available = false;
}
}
}
}
return available;
}
/// <summary>
/// Loads and executes the specified client object property.
/// </summary>
/// <typeparam name="T">the object type.</typeparam>
/// <param name="clientObject">the object.</param>
/// <param name="property">the property.</param>
/// <param name="loaded">true, if the client object was already loaded and executed at least once.</param>
/// <returns>true if available, false otherwise.</returns>
private bool LoadAndExecuteProperty<T>(T clientObject, Expression<Func<T, object>> property, bool loaded = false)
where T : ClientObject
{
string propertyName;
bool isObject;
bool isCollection;
Func<T, object> func;
Expression expression = property.Body;
if (expression is MemberExpression)
{
// Member expression, check its type to select correct property test.
propertyName = ((MemberExpression) expression).Member.Name;
isObject = typeof(ClientObject).IsAssignableFrom(property.Body.Type);
isCollection = isObject
? typeof(ClientObjectCollection).IsAssignableFrom(property.Body.Type)
: false;
func = isObject ? property.Compile() : null;
}
else if (!loaded)
{
// Unary expression or alike, test by invoking its function.
propertyName = null;
isObject = false;
isCollection = false;
func = property.Compile();
}
else
{
// Unary expression and alike should be available if just loaded.
return true;
}
int retryAttempts = 0;
int backoffInterval = Math.Max(Delay, 1);
bool retry;
bool available;
do
{
if (isObject)
{
if (clientObject.IsObjectPropertyInstantiated(propertyName))
{
ClientObject co = (ClientObject) func.Invoke(clientObject);
if (isCollection)
{
ClientObjectCollection coc = (ClientObjectCollection) co;
if (!coc.ServerObjectIsNull.HasValue || !coc.ServerObjectIsNull.Value)
{
available = coc.AreItemsAvailable;
}
else
{
available = false;
break;
}
}
else if (co.ServerObjectIsNull.HasValue)
{
available = !co.ServerObjectIsNull.Value;
break;
}
else
{
available = false;
}
}
else
{
available = false;
}
}
else if (propertyName != null)
{
available = clientObject.IsPropertyAvailable(propertyName);
}
else if (func != null)
{
try
{
func.Invoke(clientObject);
available = true;
}
catch (PropertyOrFieldNotInitializedException)
{
available = false;
}
}
else
{
available = true; // ?
}
if (!available && retryAttempts++ <= RetryCount)
{
if (retryAttempts > 1)
{
Thread.Sleep(backoffInterval * 1000);
backoffInterval *= 2;
}
Load(clientObject, property);
ExecuteQuery();
retry = true;
}
else
{
retry = false;
}
} while (retry);
return available;
}
}
The idea of using a extension is great, but only works fine with lists. The extension may choose between "object" and "scalar" properties. I think than the extension will be better this way:
public static bool IsPropertyAvailableOrInstantiated<T>(this T clientObject, Expression<Func<T, object>> property)
where T : ClientObject
{
var expression = (MemberExpression)property.Body;
var propName = expression.Member.Name;
var isObject = typeof(ClientObject).IsAssignableFrom(property.Body.Type); // test with ClientObject instead of ClientObjectList
return isObject ? clientObject.IsObjectPropertyInstantiated(propName) : clientObject.IsPropertyAvailable(propName);
}
Related
I have two models
public class NewrecordModel
{
public string NewName { get; set; }
public string NewFileName { get; set; }
public string NewFileVersion { get; set; }
}
public class OldrecordModel
{
public string OldName { get; set; }
public string OldFileName { get; set; }
public string OldFileVersion { get; set; }
}
I need to compare the property, NewName with OldName and NewFileVersion with OldFileVersion and return the difference from NewrecordModel in a list.
I tried the below one,
var unMatchedRecord = NewrecordModel.Where(o => !OldrecordModel.Any(n => n.OldName == o.NewName) || !OldrecordModel.Any(n => n.OldFileVersion == o.NewFileVersion));
The above one is returning the unmatch NewrecordModel in a list and it is working fine, but i need to compare OldFileVersion is less than NewFileVersion and return the list now. While using OldFileVersion is less than NewFileVersion in query it is listing unrelated data as output.
Below is the query i tried to compare,
var unMatchedVersion = NewrecordModel.Where(o => OldrecordModel.Any(n => n.OldFileVersion.ToInt() < o.NewFileVersion.ToInt()));
Is the above linq is correct. How to compare the numbers in linq and return the result.
I wrote this for get properties name and value what changed. I hope this help you.
public static class CompareObject
{
/// <summary>
/// Compares the properties of two objects of the same type and returns if all properties are equal.
/// </summary>
/// <param name="objectA">The first object to compare.</param>
/// <param name="objectB">The second object to compre.</param>
/// <param name="ignoreList">A list of property names to ignore from the comparison.</param>
/// <returns><c>true</c> if all property values are equal, otherwise <c>false</c>.</returns>
public static Dictionary<string,object> AreObjectsEqual(object objectA, object objectB, params string[] ignoreList)
{
//bool result;
var list = new Dictionary<string, object>();
if (objectA != null && objectB != null)
{
Type objectType;
objectType = objectA.GetType();
//result = true; // assume by default they are equal
foreach (PropertyInfo propertyInfo in objectType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead && !ignoreList.Contains(p.Name)))
{
// if it is a primative type, value type or implements IComparable, just directly try and compare the value
if (CanDirectlyCompare(propertyInfo.PropertyType))
{
object valueA;
object valueB;
valueA = propertyInfo.GetValue(objectA, null);
valueB = propertyInfo.GetValue(objectB, null);
if (!AreValuesEqual(valueA, valueB))
{
list.Add(propertyInfo.Name, valueA);
//Console.WriteLine("Mismatch with property '{0}.{1}' found.", objectType.FullName, propertyInfo.Name);
//result = false;
}
}
}
}
//else
// result = object.Equals(objectA, objectB);
return list;
}
/// <summary>
/// Determines whether value instances of the specified type can be directly compared.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>
/// <c>true</c> if this value instances of the specified type can be directly compared; otherwise, <c>false</c>.
/// </returns>
private static bool CanDirectlyCompare(Type type)
{
return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
/// <summary>
/// Compares two values and returns if they are the same.
/// </summary>
/// <param name="valueA">The first value to compare.</param>
/// <param name="valueB">The second value to compare.</param>
/// <returns><c>true</c> if both values match, otherwise <c>false</c>.</returns>
private static bool AreValuesEqual(object valueA, object valueB)
{
bool result;
IComparable selfValueComparer;
selfValueComparer = valueA as IComparable;
if (valueA == null && valueB != null || valueA != null && valueB == null)
result = false; // one of the values is null
else if (selfValueComparer != null && selfValueComparer.CompareTo(valueB) != 0)
result = false; // the comparison using IComparable failed
else if (!object.Equals(valueA, valueB))
result = false; // the comparison using Equals failed
else
result = true; // match
return result;
}
}
and to use
var changedColumns = CompareObject.AreObjectsEqual(NewrecordModel, OldrecordModel);
It might be an object from NewrecordModel collection, for which there are no objects in OldrecordModel collection with the same name OR any identically named object from OldrecordModel collection has a smaller OldFileVersion
Check the codes below:
var unMatchedVersion =
newrecordModel.Where(o => !oldrecordModel.Any(n => n.OldName == o.NewName) ||
oldrecordModel.Any(n => n.OldName == o.NewName &&
int.Parse(n.OldFileVersion) < int.Parse(o.NewFileVersion)));
I'm trying to implement a way of searching a list of objects for some search term and then returning those objects.
So far, I have managed to get it working if the search term is contained in any of the object's string properties:
IEnumerableExtensions
public static IEnumerable<T> Search<T>(this IEnumerable<T> items, string search)
{
if (!string.IsNullOrEmpty(search))
items = items.Where(i => i.Contains(search));
return items;
}
ObjectExtensions
public static bool Contains(this object inuputObject, string word)
{
return inuputObject.GetType()
.GetProperties()
.Where(x => x.PropertyType == typeof(string))
.Select(x => (string)x.GetValue(inuputObject, null))
.Where(x => x != null)
.Any(x => x.IndexOf(word, StringComparison.CurrentCultureIgnoreCase) >= 0);
}
The problem is, the objects I'm searching each contain a list of user objects, and I want to include the string properties of those users in my search.
I tried this:
public static bool Contains(this object inuputObject, string word)
{
var result = false;
var type = inuputObject.GetType();
var properties = type.GetProperties();
foreach (var property in properties)
{
if (property.PropertyType == typeof(string) && property != null)
{
var propertyValue = (string)property.GetValue(inuputObject, null);
result = propertyValue.IndexOf(word, StringComparison.CurrentCultureIgnoreCase) >= 0;
}
else
{
result = property.Contains(word);
}
if (result)
break;
}
return result;
}
But I think this is iterating around properties which I'm not interested in, and it causes the program to crash with this message in VS:
The application is in break mode
Your app has entered a break state, but there is no code to show because all threads were executing external code (typically system or framework code).
I've never seen such an error before, but I suspect it has something to do with the code running into an infinite loop, as it is checking the properties of the object, then the properties of those properties etc etc - where would that stop?
Does anyone have any suggestions for how I can achieve this?
Thank you
Your recursive call checks if the property object contains the word, not if the value of that property on your original object contains the word.
Change
result = property.Contains(word);
to
result = property.GetValue(inuputObject, null).Contains(word);
My final solution looks like this
ObjectExtensions.cs
public static class ObjectExtensions
{
/// <summary>
/// Checks each string property of the given object to check if it contains the
/// search term. If any of those properties is a collection, we search that
/// collection using the the IEnumarableExtensions Search
/// </summary>
/// <param name="inputObject"></param>
/// <param name="term"></param>
/// <returns></returns>
public static bool Contains(this object inputObject, string term)
{
var result = false;
if (inputObject == null)
return result;
var properties = inputObject
.GetType()
.GetProperties();
foreach (var property in properties)
{
// First check if the object is a string (and ensure it is not null)
if (property != null && property.PropertyType == typeof(string))
{
var propertyValue = (string)property.GetValue(inputObject, null);
result = propertyValue == null
? false
: propertyValue.IndexOf(term,
StringComparison.CurrentCultureIgnoreCase) >= 0;
}
// Otherwise, check if its a collection (we need to do this after the string
// check, as a string is technically a IEnumerable type
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
result = ((IEnumerable<object>)property
.GetValue(inputObject, null))
.Search(term).Count() > 0;
}
else
{
var propertyValue = property.GetValue(inputObject, null);
result = propertyValue == null
? false
: propertyValue.ToString().Contains(term);
}
if (result)
break;
}
return result;
}
}
IEnumerableExtensions.cs
public static class IEnumerableExtensions
{
/// <summary>
/// Extension method that searches a list of generic objects' string properties
/// for the given search term using the 'Contains' object extension
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="search"></param>
/// <returns></returns>
public static IEnumerable<T> Search<T>(this IEnumerable<T> items, string search)
{
if (!string.IsNullOrEmpty(search))
items = items.Where(i => i.Contains(search));
return items;
}
}
Usage
So to search a collection of objects for some string:
var list = new List<MyType>(){};
var results = list.Search("searchTerm");
I have a working PATCH for my user class with Delta in Web API 2. By using the .patch method I can easily detect only the changes that were sent over and then update accordingly, rather than have to receive the entire user!
The problem is there are several fields that I want to protect so they are never updated.
I saw one example on SO but it didn't leverage Delta rather seemed to be slightly more dated and practically wrote all of the patch code by hand. Is there not a way to easily tell OData's patch to skip over properties you designate (maybe I need to override patch and tell it to avoid some properties)?
How would I even begin to go about doing this (or what should I search for / research to get started)? Do action filters / validation have a role here? Do I look into model binding? Is it overriding patch?
Thanks!
Depending on what you want to do if someone tries to update protected fields you can either:
Update only fields that can be modified. For this you can construct new Delta with only these fields like this:
Delta<User> filteredDelta = new Delta<User>();
if (originalDelta.GetChangedPropertyNames().Contains("FirstName"))
{
filteredDelta.TrySetPropertyValue("FirstName", originalDelta.GetEntity().FirstName);
}
if (originalDelta.GetChangedPropertyNames().Contains("LastName"))
{
filteredDelta.TrySetPropertyValue("LastName", originalDelta.GetEntity().LastName);
}
filteredDelta.Patch(selectedUser);
Fail the PATCH request (I would say this is preferred and least surprising way to deal with such requests). This would be even simpler:
if (originalDelta.GetChangedPropertyNames().Contains("ModifiedDate"))
{
return InternalServerError(new ArgumentException("Attribue is read-only", "ModifiedDate"));
}
There's a couple of possibilities, depending on you use case...
You want to exclude the changes if they are supplied
You want to throw an error if non-editable fields are updated.
Start with an attribute to mark appropriate properties
/// <summary>
/// Marks a property as non-editable.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class NonEditableAttribute : Attribute
{
}
Then we can write some extensions against Delta to take advantage of this
public static class PatchExtensions
{
/// <summary>
/// Get the properties of a type that are non-editable.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IList<string> NonEditableProperties(this Type type)
{
return type.GetProperties().Where(x => Attribute.IsDefined(x, typeof(NonEditableAttribute))).Select(prop => prop.Name).ToList();
}
/// <summary>
/// Get this list of non-editable changes in a <see cref="Delta{T}"/>.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="delta"></param>
/// <returns></returns>
public static IList<string> NonEditableChanges<T>(this Delta<T> delta)
where T : class
{
var nec = new List<string>();
var excluded = typeof(T).NonEditableProperties();
nec.AddRange(delta.GetChangedPropertyNames().Where(x => excluded.Contains(x)));
return nec;
}
/// <summary>
/// Exclude changes from a <see cref="Delta{T}"/> based on a list of property names
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="delta"></param>
/// <param name="excluded"></param>
/// <returns></returns>
public static Delta<T> Exclude<T>(this Delta<T> delta, IList<string> excluded)
where T : class
{
var changed = new Delta<T>();
foreach (var prop in delta.GetChangedPropertyNames().Where(x => !excluded.Contains(x)))
{
object value;
if (delta.TryGetPropertyValue(prop, out value))
{
changed.TrySetPropertyValue(prop, value);
}
}
return changed;
}
/// <summary>
/// Exclude changes from a <see cref="Delta{T}"/> where the properties are marked with <see cref="NonEditableAttribute"/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="delta"></param>
/// <returns></returns>
public static Delta<T> ExcludeNonEditable<T>(this Delta<T> delta)
where T : class
{
var excluded = typeof(T).NonEditableProperties();
return delta.Exclude(excluded);
}
}
And a domain class
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
[NonEditable]
public string SecurityId { get; set; }
}
Finally your controller can then take advantage of this in the Patch method
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Customer> delta)
{
var patch = delta.ExcludeNonEditable();
// TODO: Your patching action here
}
or
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Customer> delta)
{
var nonEditable = delta.NonEditableChanges();
if (nonEditable.Count > 0)
{
throw new HttpException(409, "Cannot update as non-editable fields included");
}
// TODO: Your patching action here
}
I had the same need and I ended up writing an extension method to Delta that accepts additional parameters to limit which fields to update (similar to TryUpDateModel)
I know there must be a better way to do this, but for now this works.
I had to recreate some of the Delta private methods and classes. Most of the code is from https://github.com/mono/aspnetwebstack/blob/master/src/System.Web.Http.OData/OData/Delta.cs, https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/OData/src/System.Web.Http.OData/OData/PropertyAccessor.cs and https://github.com/mono/aspnetwebstack/blob/master/src/System.Web.Http.OData/OData/CompiledPropertyAccessor.cs (or similar, these are not the exact url's I copied from)
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Web;
using System.Reflection;
using System.Linq.Expressions;
namespace MyProject.ODataExtensions
{
public static class ODataExtensions
{
public static void Patch<TEntityType>(this System.Web.OData.Delta<TEntityType> d, TEntityType original, String[] includedProperties, String[] excludedProperties) where TEntityType : class
{
Dictionary<string, PropertyAccessor<TEntityType>> _propertiesThatExist = InitializePropertiesThatExist<TEntityType>();
var changedProperties = d.GetChangedPropertyNames();
if (includedProperties != null) changedProperties = changedProperties.Intersect(includedProperties);
if (excludedProperties != null) changedProperties = changedProperties.Except(excludedProperties);
PropertyAccessor<TEntityType>[] array = (
from s in changedProperties
select _propertiesThatExist[s]).ToArray();
var array2 = array;
for (int i = 0; i < array2.Length; i++)
{
PropertyAccessor<TEntityType> propertyAccessor = array2[i];
propertyAccessor.Copy(d.GetEntity(), original);
}
}
private static Dictionary<string, PropertyAccessor<T>> InitializePropertiesThatExist<T>() where T : class
{
Type backingType = typeof(T);
return backingType.GetProperties()
.Where(p => p.GetSetMethod() != null && p.GetGetMethod() != null)
.Select<PropertyInfo, PropertyAccessor<T>>(p => new CompiledPropertyAccessor<T>(p))
.ToDictionary(p => p.Property.Name);
}
internal abstract class PropertyAccessor<TEntityType> where TEntityType : class
{
protected PropertyAccessor(PropertyInfo property)
{
if (property == null)
{
throw new System.ArgumentException("Property cannot be null","property");
}
Property = property;
if (Property.GetGetMethod() == null || Property.GetSetMethod() == null)
{
throw new System.ArgumentException("Property must have public setter and getter", "property");
}
}
public PropertyInfo Property
{
get;
private set;
}
public void Copy(TEntityType from, TEntityType to)
{
if (from == null)
{
throw new System.ArgumentException("Argument cannot be null", "from");
}
if (to == null)
{
throw new System.ArgumentException("Argument cannot be null", "to");
}
SetValue(to, GetValue(from));
}
public abstract object GetValue(TEntityType entity);
public abstract void SetValue(TEntityType entity, object value);
}
internal class CompiledPropertyAccessor<TEntityType> : PropertyAccessor<TEntityType> where TEntityType : class
{
private Action<TEntityType, object> _setter;
private Func<TEntityType, object> _getter;
public CompiledPropertyAccessor(PropertyInfo property)
: base(property)
{
_setter = MakeSetter(Property);
_getter = MakeGetter(Property);
}
public override object GetValue(TEntityType entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
return _getter(entity);
}
public override void SetValue(TEntityType entity, object value)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_setter(entity, value);
}
private static Action<TEntityType, object> MakeSetter(PropertyInfo property)
{
Type type = typeof(TEntityType);
ParameterExpression entityParameter = Expression.Parameter(type);
ParameterExpression objectParameter = Expression.Parameter(typeof(Object));
MemberExpression toProperty = Expression.Property(Expression.TypeAs(entityParameter, property.DeclaringType), property);
UnaryExpression fromValue = Expression.Convert(objectParameter, property.PropertyType);
BinaryExpression assignment = Expression.Assign(toProperty, fromValue);
Expression<Action<TEntityType, object>> lambda = Expression.Lambda<Action<TEntityType, object>>(assignment, entityParameter, objectParameter);
return lambda.Compile();
}
private static Func<TEntityType, object> MakeGetter(PropertyInfo property)
{
Type type = typeof(TEntityType);
ParameterExpression entityParameter = Expression.Parameter(type);
MemberExpression fromProperty = Expression.Property(Expression.TypeAs(entityParameter, property.DeclaringType), property);
UnaryExpression convert = Expression.Convert(fromProperty, typeof(Object));
Expression<Func<TEntityType, object>> lambda = Expression.Lambda<Func<TEntityType, object>>(convert, entityParameter);
return lambda.Compile();
}
}
}
}
I have a foreach loop in which I need to verify whether the element is last in the collection I'm iterating on. What I've tried:
foreach (var character in list) // list is of type `string`
{
if (character == list.Last())
{
}
}
But in this case if I have "la la la" the if statement will execute on a second character.
Question: How to write the if statement so that it will execute when accessing the last element of a sequence?
I would advise iterating using the index rather than the object reference i.e.
for (int i = 0; i <= list.Count-1; i++)
{
if (i == list.Count-1)
{
// so something special with last item
}
}
foreach (var character in list) // list is of type `string`
{
if (character == list[list.Count - 1])
{
}
}
Here is a DEMO.
As an alternative, since List implements IEnumerable interface, you can use Enumerable.Last method
Returns the last element of a sequence.
foreach (var character in list) // list is of type `string`
{
if (character == list.Last())
{
}
}
Here is a DEMO.
Since your list is actually a string, you need to convert it into a list.
var elementList = list.Split(" ");
You can then find the last element.
var lastElement = elementList.LastOrDefault();
Just check using IsNullOrEmpty to handle the case of an empty list.
If you only want to do an action on the last character, then Mare Infinitus' code should do the trick.
What about:
var elementList = list.Split(" ");
if (elementList.Last().Equals(character))
{
// do something here
}
this should do it, no need for foreach.
However, if you want to loop and do a specific action for the last character, then you can use a standard for loop. James' answer:
for (int i = 0; i <= list.Count-1; i++)
{
if (i == list.Count-1)
{
// so something special with last item
}
}
If you find yourself doing this kind of thing often, you can use an extension method which will let you ask if an element in a sequence is the last item.
This was originally written by Jon Skeet; he called it "Smart Enumerable", and I believe it is part of the MoreLinq Linq Extensions (also by Jon Skeet).
If you do use such a thing, your code would looks something like this:
foreach (var character in list.AsSmartEnumerable())
if (character.IsLast)
// Do something with character.Value
Here's a slightly modified copy of Jon Skeet's implementation:
/// <summary>
/// Static class to make creation easier. If possible though, use the extension
/// method in SmartEnumerableExt.
/// </summary>
public static class SmartEnumerable
{
/// <summary> method to make life easier.</summary>
/// <typeparam name="T">Type of enumerable</typeparam>
/// <param name="source">Source enumerable</param>
/// <returns>A new SmartEnumerable of the appropriate type</returns>
public static SmartEnumerable<T> Create<T>(IEnumerable<T> source)
{
return new SmartEnumerable<T>(source);
}
}
/// <summary>Wrapper methods for SmartEnumerable[T].</summary>
public static class SmartEnumerableExt
{
/// <summary>Extension method to make life easier.</summary>
/// <typeparam name="T">Type of enumerable</typeparam>
/// <param name="source">Source enumerable</param>
/// <returns>A new SmartEnumerable of the appropriate type</returns>
public static SmartEnumerable<T> AsSmartEnumerable<T>(this IEnumerable<T> source)
{
return new SmartEnumerable<T>(source);
}
}
/// <summary>
/// Type chaining an IEnumerable<T> to allow the iterating code
/// to detect the first and last entries simply.
/// </summary>
/// <typeparam name="T">Type to iterate over</typeparam>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification="This is too general to end in 'Collection'")]
public class SmartEnumerable<T>: IEnumerable<SmartEnumerable<T>.Entry>
{
/// <summary>Enumerable to which we proxy</summary>
readonly IEnumerable<T> _enumerable;
/// <summary>Constructor.</summary>
/// <param name="enumerable">Collection to enumerate. Must not be null.</param>
public SmartEnumerable(IEnumerable<T> enumerable)
{
if (enumerable==null)
{
throw new ArgumentNullException("enumerable");
}
this._enumerable = enumerable;
}
/// <summary>
/// Returns an enumeration of Entry objects, each of which knows
/// whether it is the first/last of the enumeration, as well as the
/// current value.
/// </summary>
public IEnumerator<Entry> GetEnumerator()
{
using (IEnumerator<T> enumerator = _enumerable.GetEnumerator())
{
if (!enumerator.MoveNext())
{
yield break;
}
bool isFirst = true;
bool isLast = false;
int index = 0;
while (!isLast)
{
T current = enumerator.Current;
isLast = !enumerator.MoveNext();
yield return new Entry(isFirst, isLast, current, index++);
isFirst = false;
}
}
}
/// <summary>Non-generic form of GetEnumerator.</summary>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Represents each entry returned within a collection,
/// containing the value and whether it is the first and/or
/// the last entry in the collection's. enumeration
/// </summary>
public class Entry
{
internal Entry(bool isFirst, bool isLast, T value, int index)
{
this._isFirst = isFirst;
this._isLast = isLast;
this._value = value;
this._index = index;
}
/// <summary>The value of the entry.</summary>
public T Value
{
get
{
return _value;
}
}
/// <summary>Whether or not this entry is first in the collection's enumeration.</summary>
public bool IsFirst
{
get
{
return _isFirst;
}
}
/// <summary>Whether or not this entry is last in the collection's enumeration.</summary>
public bool IsLast
{
get
{
return _isLast;
}
}
/// <summary>The 0-based index of this entry (i.e. how many entries have been returned before this one)</summary>
public int Index
{
get
{
return _index;
}
}
readonly bool _isFirst;
readonly bool _isLast;
readonly T _value;
readonly int _index;
}
}
If you only have an IEnumerable, you have to manually trigger the enumerator. Here is a sample extension method which maybe helps you:
public static class IEnumerableExtensions
{
public static void Action<T>(this IEnumerable<T> source, Action<T> sequenceElement, Action<T> lastElement)
{
if (source == null)
throw new ArgumentNullException("source");
if (sequenceElement == null)
throw new ArgumentNullException("sequenceElement");
if (lastElement == null)
throw new ArgumentNullException("lastElement");
T element = default(T);
using (var enumerator = source.GetEnumerator())
{
if (enumerator.MoveNext())
element = enumerator.Current;
while (enumerator.MoveNext())
{
sequenceElement(element);
element = enumerator.Current;
}
lastElement(element);
}
}
}
You can then call it this way:
var myElements = Enumerable.Range(1, 10);
myElements.Action(value => Console.WriteLine("Something out of the sequence: " + value),
value => Console.WriteLine("The last element: " + value));
I have some classes with lots of simple properties (from a datamodel I have no control over) -- I'd like to be able to find if the new version of an object is the same as an old version, but don't want to do 20 different "IsEqual" methods (I don't really like the "IsEqual" name because it is not an analog to ==). Another wrinkle, in most of the cases I don't want it to do a deep compare, but in some cases I do want that.
I'd like something along the lines of:
//Property could be PropertyInfo if that is necessary
bool IsEqual<T>(T first, T second, List<Property> ignorableProperties=emptyList, bool recurse=false)
{
//the comparison code returning if they are equal ignoring
//the properties in the ignorableProperties list, recursing if recurse == true
//not sure how I'd handle the comparison of sub-objects in the recursive step.
}
public static bool AreEqual<T>(this T first, T second,
bool recurse = false, params string[] propertiesToSkip)
{
if (Equals(first, second)) return true;
if (first == null)
return second == null;
else if (second == null)
return false;
if (propertiesToSkip == null) propertiesToSkip = new string[] { };
var properties = from t in first.GetType().GetProperties()
where t.CanRead
select t;
foreach (var property in properties)
{
if (propertiesToSkip.Contains(property.Name)) continue;
var v1 = property.GetValue(first, null);
var v2 = property.GetValue(second, null);
if (recurse)
if (!AreEqual(v1, v2, true, propertiesToSkip))
return false;
else
continue;
if (!Equals(v1, v2)) return false;
}
return true;
}
Here's something along those lines from our code base. It uses a list of properties to compare, rather than a list of properties to ignore. Then it returns a list of which properties did not match:
public static List<PropertyInfo> CompareObjects<T>(T o1, T o2, List<PropertyInfo> props) where T : class
{
var type = typeof(T);
var mismatched = CompareObjects(type, o1, o2, props);
return mismatched;
}
public static List<PropertyInfo> CompareObjects(Type t, object o1, object o2, List<PropertyInfo> props)
{
List<PropertyInfo> mismatched = null;
foreach (PropertyInfo prop in props)
{
if (prop.GetValue(o1, null) == null && prop.GetValue(o2, null) == null) ;
else if (
prop.GetValue(o1, null) == null || prop.GetValue(o2, null) == null ||
!prop.GetValue(o1, null).Equals(prop.GetValue(o2, null)))
{
if (mismatched == null) mismatched = new List<PropertyInfo>();
mismatched.Add(prop);
}
}
return mismatched;
}
If you wanted an IsEqual method it would be a matter of returning true/false when you find mismatched properties instead.
Hope this helps!
Here's how we achieve this in the Umbraco Framework, with a base class called AbstractEquatableObject which is a modified version of Sharp Architecture's BaseObject http://umbraco.codeplex.com/SourceControl/changeset/view/2b4d693de19c#Source%2fLibraries%2fUmbraco.Framework%2fAbstractEquatableObject.cs
Implementors override GetMembersForEqualityComparison() and the base class caches the PropertyInfo objects once per Type for the application in a ConcurrentDictionary<Type, IEnumerable<PropertyInfo>>.
I've pasted the class here, although it refers to LogHelper elsewhere in the Framework so you can remove that (or just use our Framework lib, there's other useful stuff in there).
If you want a helper for getting a PropertyInfo from an expression, to avoid magic strings all over the place (e.g. replaced with x => x.MyProperty), check out the GetPropertyInfo methods of our ExpressionHelper at http://umbraco.codeplex.com/SourceControl/changeset/view/2b4d693de19c#Source%2fLibraries%2fUmbraco.Framework%2fExpressionHelper.cs
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Umbraco.Framework.Diagnostics;
namespace Umbraco.Framework
{
/// <summary>
/// Objects implementing <see cref="AbstractEquatableObject{T}"/> are provided with common functionality for establishing domain-specific equality
/// and a robust implementation of GetHashCode
/// </summary>
/// <typeparam name="T"></typeparam>
[Serializable]
public abstract class AbstractEquatableObject<T> where T : AbstractEquatableObject<T>
{
/// <summary>
/// Returns the real type in case the <see cref="object.GetType" /> method has been proxied.
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
protected internal virtual Type GetNativeType()
{
// Returns the real type in case the GetType method has been proxied
// See http://groups.google.com/group/sharp-architecture/browse_thread/thread/ddd05f9baede023a for clarification
return GetType();
}
/// <summary>Returns a hash code for this instance.</summary>
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. </returns>
public override int GetHashCode()
{
unchecked
{
// Based on an algorithm set out at http://sharp-architecture.googlecode.com/svn/trunk/src/SharpArch/SharpArch.Core/DomainModel/BaseObject.cs
var naturalIdMembers = EnsureEqualityComparisonMembersCached();
// It's possible for two objects to return the same hash code based on
// identically valued properties, even if they're of two different types,
// so we include the object's type in the hash calculation
var hashCode = GetType().GetHashCode();
if (!naturalIdMembers.Any()) return base.GetHashCode();
foreach (var value in naturalIdMembers
.Select(x => x.GetValue(this, null))
.Where(x => !ReferenceEquals(x, null)))
{
// Check if the property value is null or default (e.g. Guid.Empty)
// In which case we just want to use the base GetHashCode because we have no other way
// of determining if the instances are different
if (value.Equals(value.GetType().GetDefaultValue()))
hashCode = (hashCode * 41) ^ base.GetHashCode();
else
hashCode = (hashCode * 41) ^ value.GetHashCode();
}
return hashCode;
}
}
/// <summary>Determines whether the specified object is equal to this instance.</summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj)
{
if (ReferenceEquals(obj, null)) return false;
var incoming = obj as AbstractEquatableObject<T>;
if (ReferenceEquals(incoming, null)) return false;
if (ReferenceEquals(this, incoming)) return true;
// (APN Oct 2011) Disabled the additional check for GetNativeType().Equals(incoming.GetNativeType())
// so that we can compare RelationById with Relation using Equals however this may need reinstating
// and using IComparable instead
return CompareCustomEqualityMembers(incoming);
}
/// <summary>
/// Implements the operator ==.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns>The result of the operator.</returns>
/// <remarks></remarks>
public static bool operator ==(AbstractEquatableObject<T> left, AbstractEquatableObject<T> right)
{
// If both are null, or both are same instance, return true.
if (ReferenceEquals(left, right)) return true;
// If one is null, but not both, return false.
if (((object)left == null) || ((object)right == null)) return false;
return left.Equals(right);
}
/// <summary>
/// Implements the operator !=.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns>The result of the operator.</returns>
/// <remarks></remarks>
public static bool operator !=(AbstractEquatableObject<T> left, AbstractEquatableObject<T> right)
{
return !(left == right);
}
/// <summary>
/// A static <see cref="ConcurrentDictionary{Type, IEnumerable{PropertyInfo}}"/> cache of natural ids for types which may implement this abstract class.
/// </summary>
protected readonly static ConcurrentDictionary<Type, IEnumerable<PropertyInfo>> EqualityComparisonMemberCache = new ConcurrentDictionary<Type, IEnumerable<PropertyInfo>>();
/// <summary>
/// Gets the natural id members.
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
protected abstract IEnumerable<PropertyInfo> GetMembersForEqualityComparison();
/// <summary>
/// Ensures the natural id members are cached in the static <see cref="EqualityComparisonMemberCache"/>.
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
protected internal virtual IEnumerable<PropertyInfo> EnsureEqualityComparisonMembersCached()
{
return EqualityComparisonMemberCache.GetOrAdd(GetNativeType(), x => GetMembersForEqualityComparison());
}
/// <summary>
/// Establishes if the natural id of this instance matches that of <paramref name="compareWith"/>
/// </summary>
/// <param name="compareWith">The instance with which to compare.</param>
/// <returns></returns>
/// <remarks></remarks>
protected internal virtual bool CompareCustomEqualityMembers(AbstractEquatableObject<T> compareWith)
{
// Standard input checks - if it's the same instance, or incoming is null, etc.
if (ReferenceEquals(this, compareWith)) return true;
if (ReferenceEquals(compareWith, null)) return false;
// Get the natural id spec
var naturalIdMembers = EnsureEqualityComparisonMembersCached();
// If the overriding objct hasn't specified a natural id, just return the base Equals implementation
if (!naturalIdMembers.Any()) return base.Equals(compareWith);
// We have a natural id specified, so compare the members
foreach (var naturalIdMember in naturalIdMembers)
{
try
{
// Get the property values of this instance and the incoming instance
var localValue = naturalIdMember.GetValue(this, null);
var incomingValue = naturalIdMember.GetValue(compareWith, null);
// If the property values refere to the same instance, or both refer to null, continue the loop
if (ReferenceEquals(localValue, incomingValue) || (ReferenceEquals(localValue, null) && ReferenceEquals(incomingValue, null)))
continue;
// If this property value doesn't equal the incoming value, the comparison fails so we can return straight away
if (!localValue.Equals(incomingValue)) return false;
}
catch (Exception ex)
{
// If there was an error accessing one of the properties, log it and return false
LogHelper.TraceIfEnabled<AbstractEquatableObject<T>>("Error comparing {0} to {1}: {2}",
() => GetNativeType().Name,
() => compareWith.GetNativeType().Name,
() => ex.Message);
return false;
}
}
// To get this far means we haven't had any misses, so return true
return true;
}
}
}