Covariance/Contravariance with a linq expression - c#

I have a function called "CreateCriteriaExpression" that takes a json string and creates a linq expression from it.
This method is called by another called "GetByCriteria", which calls the "CreateCriteriaExpression" method and then executes that expression against an entity framework context.
For all of my entity framework objects, the "GetByCriteria" method is identical except for it's types. So I am trying to convert it to use generics instead of hard coded types.
When the "GetByCriteria" method gets to the point that it has to call the "CreateCriteriaExpression" method, I am having it use a factory class to determine the appropriate class/method to use. Then in the "linq expression" class, the linq expression for a particular type is created and returned.
The problem I am having is that the linq expression has to be created for a specific type, but the return value is of the generic type and it won't automatically convert between the two, even though the one is a parent of the other (covariance).
Is there any way I can make this work?
Some example code:
The "GetByCriteria" method:
/// <summary>
/// Gets a <see cref="System.Collections.Generic.List"/> of <see cref="TEntity"/>
/// objects that match the passed JSON string.
/// </summary>
/// <param name="myCriteria">A list of JSON strings containing a key/value pair of "parameterNames" and "parameterValues".</param>
/// <param name="myMatchMethod">Defines which matching method to use when finding matches on the <paramref name="myCriteria"/>.</param>
/// <returns>
/// A <see cref="System.Collections.Generic.List"/> of <see cref="TEntity"/>
/// objects.
/// </returns>
/// <seealso cref="TEntity"/>
///
/// <seealso cref="Common.MultipleCriteriaMatchMethod"/>
/// <remarks>
/// This method takes a <see cref="System.Collections.Generic.List"/> of JSON strings, and a <see cref="Common.MultipleCriteriaMatchMethod"/> and returns a
/// <see cref="System.Collections.Generic.List"/> of all matching
/// <see cref="TEntity"/> objects from the back-end database. The <paramref name="myMatchMethod"/> is used to determine how to match when multiple <paramref name="myCriteria"/> are passed. You can require that any results must match on ALL the passed JSON criteria, or on ANY of the passed criteria. This is essentially an "AND" versus and "OR" comparison.
/// </remarks>
[ContractVerification(true)]
public static List<TEntity> GetByCriteria<TContext, TEntity>(List<string> myCriteria, Common.MultipleCriteriaMatchMethod myMatchMethod)
where TContext : System.Data.Objects.ObjectContext, new()
where TEntity : System.Data.Objects.DataClasses.EntityObject
{
// Setup Contracts
Contract.Requires(myCriteria != null);
TContext db = new TContext();
// Intialize return variable
List<TEntity> result = null;
// Initialize working variables
// Set the predicates to True by default (for "AND" matches)
var predicate = PredicateBuilder.True<TEntity>();
var customPropertiesPredicate = PredicateBuilder.True<TEntity>();
// Set the predicates to Falase by default (for "OR" matches)
if (myMatchMethod == Common.MultipleCriteriaMatchMethod.MatchOnAny)
{
predicate = PredicateBuilder.False<TEntity>();
customPropertiesPredicate = PredicateBuilder.False<TEntity>();
}
// Loop over each Criteria object in the passed list of criteria
foreach (string x in myCriteria)
{
// Set the Criteria to local scope (sometimes there are scope problems with LINQ)
string item = x;
if (item != null)
{
JsonLinqParser parser = JsonLinqParserFactory.GetParser(typeof(TEntity));
// If the designated MultipleCriteriaMatchMethod is "MatchOnAll" then use "AND" statements
if (myMatchMethod == Common.MultipleCriteriaMatchMethod.MatchOnAll)
{
predicate = predicate.Expand().And<TEntity>(parser.CreateCriteriaExpression<TEntity>(item).Expand());
customPropertiesPredicate = customPropertiesPredicate.Expand().And<TEntity>(parser.CreateCriteriaExpressionForCustomProperties<TEntity>(item).Expand());
}
// If the designated MultipleCriteriaMatchMethod is "MatchOnAny" then use "OR" statements
else if (myMatchMethod == Common.MultipleCriteriaMatchMethod.MatchOnAny)
{
predicate = predicate.Expand().Or<TEntity>(parser.CreateCriteriaExpression<TEntity>(item).Expand());
customPropertiesPredicate = customPropertiesPredicate.Expand().Or<TEntity>(parser.CreateCriteriaExpressionForCustomProperties<TEntity>(item).Expand());
}
}
}
// Set a temporary var to hold the results
List<TEntity> qry = null;
// Set some Contract Assumptions to waive Static Contract warnings on build
Contract.Assume(predicate != null);
Contract.Assume(customPropertiesPredicate != null);
// Run the query against the backend database
qry = db.CreateObjectSet<TEntity>().AsExpandable<TEntity>().Where<TEntity>(predicate).ToList<TEntity>();
//qry = db.CreateObjectSet<TEntity>().Where(predicate).ToList<TEntity>();
// Run the query for custom properties against the resultset obtained from the database
qry = qry.Where<TEntity>(customPropertiesPredicate.Compile()).ToList<TEntity>();
// Verify that there are results
if (qry != null && qry.Count != 0)
{
result = qry;
}
// Return the results
return result;
}
The JsonLinqParser class (does not build):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LinqKit;
using Newtonsoft.Json.Linq;
namespace DAL
{
internal class JsonLinqParser_Paser : JsonLinqParser
{
internal override System.Linq.Expressions.Expression<Func<TEntity, bool>> CreateCriteriaExpression<TEntity>(string myCriteria)
{
var predicate = PredicateBuilder.True<BestAvailableFIP>();
JObject o = JObject.Parse(myCriteria);
// bmp
decimal _bmp;
if (o["bmp"] != null && decimal.TryParse((string)o["bmp"], out _bmp))
{
predicate = predicate.And<BestAvailableFIP>(x => x.bmp == _bmp);
}
// COUNTY
if (!string.IsNullOrWhiteSpace((string)o["COUNTY"]))
{
string _myStringValue = (string)o["COUNTY"];
predicate = predicate.And<BestAvailableFIP>(x => x.COUNTY.Contains(_myStringValue));
}
// emp
decimal _emp;
if (o["emp"] != null && decimal.TryParse((string)o["emp"], out _emp))
{
predicate = predicate.And<BestAvailableFIP>(x => x.emp == _emp);
}
// FIPSCO_STR
if (!string.IsNullOrWhiteSpace((string)o["FIPSCO_STR"]))
{
string _myStringValue = (string)o["FIPSCO_STR"];
predicate = predicate.And<BestAvailableFIP>(x => x.FIPSCO_STR.Contains(_myStringValue));
}
// FIPSCODE
double _FIPSCODE;
if (o["FIPSCODE"] != null && double.TryParse((string)o["FIPSCODE"], out _FIPSCODE))
{
predicate = predicate.And<BestAvailableFIP>(x => x.FIPSCODE == _FIPSCODE);
}
// FROMDESC
if (!string.IsNullOrWhiteSpace((string)o["FROMDESC"]))
{
string _myStringValue = (string)o["FROMDESC"];
predicate = predicate.And<BestAvailableFIP>(x => x.FROMDESC.Contains(_myStringValue));
}
// LANEMI
decimal _LANEMI;
if (o["LANEMI"] != null && decimal.TryParse((string)o["LANEMI"], out _LANEMI))
{
predicate = predicate.And<BestAvailableFIP>(x => x.LANEMI == _LANEMI);
}
// MPO_ABBV
if (!string.IsNullOrWhiteSpace((string)o["MPO_ABBV"]))
{
string _myStringValue = (string)o["MPO_ABBV"];
predicate = predicate.And<BestAvailableFIP>(x => x.MPO_ABBV.Contains(_myStringValue));
}
// owner
if (!string.IsNullOrWhiteSpace((string)o["owner"]))
{
string _myStringValue = (string)o["owner"];
predicate = predicate.And<BestAvailableFIP>(x => x.owner.Contains(_myStringValue));
}
// PASER
decimal _PASER;
if (o["PASER"] != null && decimal.TryParse((string)o["PASER"], out _PASER))
{
predicate = predicate.And<BestAvailableFIP>(x => x.PASER == _PASER);
}
// PASER_GROUP
if (!string.IsNullOrWhiteSpace((string)o["PASER_GROUP"]))
{
string _myStringValue = (string)o["PASER_GROUP"];
predicate = predicate.And<BestAvailableFIP>(x => x.PASER_GROUP.Contains(_myStringValue));
}
// pr
decimal _pr;
if (o["pr"] != null && decimal.TryParse((string)o["pr"], out _pr))
{
predicate = predicate.And<BestAvailableFIP>(x => x.pr == _pr);
}
// RDNAME
if (!string.IsNullOrWhiteSpace((string)o["RDNAME"]))
{
string _myStringValue = (string)o["RDNAME"];
predicate = predicate.And<BestAvailableFIP>(x => x.RDNAME.Contains(_myStringValue));
}
// SPDR_ABBV
if (!string.IsNullOrWhiteSpace((string)o["SPDR_ABBV"]))
{
string _myStringValue = (string)o["SPDR_ABBV"];
predicate = predicate.And<BestAvailableFIP>(x => x.SPDR_ABBV.Contains(_myStringValue));
}
// TODESC
if (!string.IsNullOrWhiteSpace((string)o["TODESC"]))
{
string _myStringValue = (string)o["TODESC"];
predicate = predicate.And<BestAvailableFIP>(x => x.TODESC.Contains(_myStringValue));
}
// TYPE
if (!string.IsNullOrWhiteSpace((string)o["TYPE"]))
{
string _myStringValue = (string)o["TYPE"];
predicate = predicate.And<BestAvailableFIP>(x => x.TYPE.Contains(_myStringValue));
}
return predicate;
}
internal override System.Linq.Expressions.Expression<Func<TEntity, bool>> CreateCriteriaExpressionForCustomProperties<TEntity>(string myCriteria)
{
var predicate = PredicateBuilder.True<TEntity>();
return predicate;
}
}
}
The JsonLinqParser base class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace DAL
{
abstract class JsonLinqParser
{
abstract internal Expression<Func<TEntity, bool>> CreateCriteriaExpression<TEntity>(string myCriteria)
where TEntity : System.Data.Objects.DataClasses.EntityObject;
abstract internal Expression<Func<TEntity, bool>> CreateCriteriaExpressionForCustomProperties<TEntity>(string myCriteria)
where TEntity : System.Data.Objects.DataClasses.EntityObject;
}
}
The factory class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DAL
{
internal static class JsonLinqParserFactory
{
internal static JsonLinqParser GetParser(Type type)
{
switch (type.Name)
{
case "BestAvailableFIP":
return new JsonLinqParser_Paser();
default:
//if we reach this point then we failed to find a matching type. Throw
//an exception.
throw new Exception("Failed to find a matching JsonLinqParser in JsonLinqParserFactory.GetParser() - Unknown Type: " + type.Name);
}
}
}
}

The problem is that JsonLinqParser_Paser's signatures are generic, type-agnostic, and your implementation is its specialization for concrete BestAvailableFIP type. This is not a covariance issue, it's just type incompatibility (at compiler level).
The solution is to make JsonLinqParser to be generic type (not having generic methods) - or even an interface, then make JsonLinqParser_Paser to implement JsonLinqParser<BestAvailableFIP>. We'll then have everything matching.
The IJsonLinqParser interface:
interface IJsonLinqParser<TEntity>
where TEntity : System.Data.Objects.DataClasses.EntityObject
{
Expression<Func<TEntity, bool>> CreateCriteriaExpression(string myCriteria);
Expression<Func<TEntity, bool>> CreateCriteriaExpressionForCustomProperties(string myCriteria)
}
The implementation - signatures for JsonLinqParser_Paser:
internal class JsonLinqParser_Paser : IJsonLinqParser<BestAvailableFIP>
{
public Expression<Func<BestAvailableFIP, bool>> CreateCriteriaExpression(string myCriteria)
{
// implementation as yours
}
public Expression<Func<BestAvailableFIP, bool>> CreateCriteriaExpressionForCustomProperties(string myCriteria)
{
// implementation as yours
}
}
The factory needs to return IJsonLinqParser<TEntity>, what is not a problem as we know TEntity there:
internal static class JsonLinqParserFactory
{
internal static IJsonLinqParser<TEntity> GetParser<TEntity>()
where TEntity : System.Data.Objects.DataClasses.EntityObject
{
switch (typeof(TEntity).Name)
{
case "BestAvailableFIP":
return (IJsonLinqParser<TEntity>) new JsonLinqParser_Paser();
default:
//if we reach this point then we failed to find a matching type. Throw
//an exception.
throw new Exception("Failed to find a matching JsonLinqParser in JsonLinqParserFactory.GetParser() - Unknown Type: " + typeof(TEntity).Name);
}
}
}
And finally in GetByCriteria you can have:
IJsonLinqParser<TEntity> parser = JsonLinqParserFactory.GetParser<TEntity>();
No need for <TEntity> in parser method calls now, as parser is already TEntity-specific.
Hope this helps.
By the way, your factory infrastructure can be easily replaced by good IoC tool.

Related

Dll Private method throw "not accesible due to access level" WITHIN owner class

I'm an amateur and not English native speaker. I'm trying to do mapper for use it with Dapper's dynamic results, I need it for a personal project and I couldn't adapt self Dapper mapper either Automapper to my needs (probably due to my lack of knowledge), so I decided to make mine.
It works flawlessly when I add the mapper code with "Add -> Existing Item", but if I try to use it as a DLL it throws the "not accessible due to access level" exception, pointing this part of the code:
public T Map(IEnumerable<dynamic> dapperResult, bool cleanResult = false)
{
var parser = new PrePostFixesParser(this);
T mapped = this.NewObject(dapperResult.First()); <------//EXCEPTION HERE
if (_OnlyConstructor.Contains(this.TType)) return mapped;
//... Other things
The method NewObject:
private T NewObject(dynamic dyn)
{
Type t = typeof(T);
bool IsInterfaceNotIEnumerable = t.IsInterface
&& !typeof(IDictionary).IsAssignableFrom(t)
&& !(typeof(IEnumerable).IsAssignableFrom(t) && !typeof(string).IsAssignableFrom(t));
if (IsInterfaceNotIEnumerable)
throw new CustomException_DapperMapper(
#"DapperMapper.NewObject: Exception trying to instantiate an interface that isn't an IEnumerable. This is a BUG.");
T newObj;
//if there are a constructor configurated
if (_Constructors.ContainsKey(this.TType))
{
//object[] constructorParams = GetConstructorParams();
try
{
Func<dynamic, T> constDelegate = (Func<dynamic, T>)_Constructors[this.TType];
newObj = constDelegate(dyn);
}
catch (Exception err)
{
throw new CustomException_DapperMapper(
$#"DapperMapper.NewObject: Exception using constructor to create object of type {TType.Name} with delegate {_Constructors[TType]}.", err);
}
}
//if there are no constructor configurated, use parameterless constructor
else newObj = Activator.CreateInstance<T>();
return newObj;
}
Both methods are part of the same class called DapperMapper. I've read the page always want all the possible code in the same question, so I'm posting the entire class here, but it's a large one, so I'm posting too the link to Github where I have all the solution... just in case:
public class DapperMapper<T> : DMStatic_Mapper, iDapperMapper
{
public DapperMapper(MapperStore store)
{
this.TType = typeof(T);
this.MappersStore = store;
this.MappersStore.StoreMapper(this.TType, this);
}
#region properties
public MapperStore MappersStore { get; private set; }
public Dictionary<MemberInfo, MemberTypeInfo> mtInfos { get { return _mtInfos[this.TType]; } }
public Type TType { get; private set; }
public IEnumerable<string> NamesList { get { return _NamesList[this.TType]; } }
public Tuple<string[], bool> Prefixes { get { return _Prefixes.ContainsKey(this.TType) ? _Prefixes[this.TType] : null; } }
public Tuple<string[], bool> Postfixes { get { return _Postfixes.ContainsKey(this.TType) ? _Postfixes[this.TType] : null; } }
#endregion
#region helpers
private T NewObject(dynamic dyn)
{
Type t = typeof(T);
bool IsInterfaceNotIEnumerable = t.IsInterface
&& !typeof(IDictionary).IsAssignableFrom(t)
&& !(typeof(IEnumerable).IsAssignableFrom(t) && !typeof(string).IsAssignableFrom(t));
if (IsInterfaceNotIEnumerable)
throw new CustomException_DapperMapper(
#"DapperMapper.NewObject: Exception trying to instantiate an interface that isn't an IEnumerable. This is a BUG.");
T newObj;
//if there are a constructor configurated
if (_Constructors.ContainsKey(this.TType))
{
//object[] constructorParams = GetConstructorParams();
try
{
Func<dynamic, T> constDelegate = (Func<dynamic, T>)_Constructors[this.TType];
newObj = constDelegate(dyn);
}
catch (Exception err)
{
throw new CustomException_DapperMapper(
$#"DapperMapper.NewObject: Exception using constructor to create object of type {TType.Name}
with delegate {_Constructors[TType]}.", err);
}
}
//if there are no constructor configurated, use parameterless constructor
else newObj = Activator.CreateInstance<T>();
return newObj;
}
#endregion
#region public methods
/// <summary>
/// Remove duplicated results due to JOINs.
/// </summary>
/// <param name="origDapperResult"></param>
/// <param name="cleanResult"></param>
/// <returns></returns>
public IEnumerable<dynamic> GetDistinctDapperResult(IEnumerable<dynamic> origDapperResult, bool cleanResult)
{
PrePostFixesParser parser = new PrePostFixesParser(this);
IEnumerable<string> names = this.NamesList;
List<dynamic> result = new List<dynamic>();
foreach(dynamic dyn in origDapperResult)
{
IDictionary<string, object> dict =
(!cleanResult ? parser.GetTypeMembersWithoutPrePostFixes(dyn, names) : dyn)
as IDictionary<string, object>;
bool distinct = true;
foreach(dynamic resultDyn in result)
{
IDictionary<string, object> resDict = resultDyn as IDictionary<string, object>;
if(dict.Keys.SequenceEqual(resDict.Keys) && dict.Values.SequenceEqual(resDict.Values))
{
distinct = false;
break;
}
}
if (distinct) result.Add(dyn);
}
return result;
}
/// <summary>
/// Check if the dynamic object have all the members needed to map a new T object, except those setted as IEnumerable,
/// which should be provided in others dynamic.
/// </summary>
/// <param name="dyn"></param>
/// <returns></returns>
public bool CheckIfDynamicHasAllTypeMembersByName(dynamic dyn)
{
IDictionary<string, object> membersDict = dyn as IDictionary<string, object>;
IEnumerable<string> dynList = membersDict.Select(kvp => kvp.Key);
PrePostFixesParser parser = new PrePostFixesParser(this);
IEnumerable<string> list = parser.GetCleanNamesList(this.NamesList);
return !dynList.Except(list).Any() && !list.Except(dynList).Any();
}
/// <summary>
/// Check if the dynamic object have all the members needed to map a new T object, except those setted as IEnumerable,
/// which should be provided in others dynamic.
/// </summary>
/// <param name="membersDict"></param>
/// <returns></returns>
public bool CheckIfDynamicHasAllTypeMembersByName(IDictionary<string, object> membersDict)
{
IEnumerable<string> dynList = membersDict.Select(kvp => kvp.Key);
PrePostFixesParser parser = new PrePostFixesParser(this);
return dynList.SequenceEqual(parser.GetCleanNamesList(this.NamesList));
}
/// <summary>
/// Generic Map.
/// </summary>
/// <param name="dapperResult"></param>
/// <returns></returns>
public T Map(IEnumerable<dynamic> dapperResult, bool cleanResult = false)
{
var parser = new PrePostFixesParser(this);
T mapped = this.NewObject(dapperResult.First());
if (_OnlyConstructor.Contains(this.TType)) return mapped;
//TODO: divide el siguiente foreach en dos con dos nuevos diccionarios estáticos, uno para pInfos y otro para fInfos,
//aunque se repita código: hacer métodos para cada parte del código del tipo:
//private T PreMapCreator(KeyValuePair<PropertyInfo, MemberTypeInfo> kvp, IEnumerable<dynamic> dapperResult, bool cleanResult = false)
//private T PreMapIEnumerable(KeyValuePair<PropertyInfo, MemberTypeInfo> kvp, IEnumerable<dynamic> dapperResult, bool cleanResult = false)
//...
//Loop through all members
foreach (KeyValuePair<MemberInfo, MemberTypeInfo> kvp in mtInfos)
{
if (kvp.Value == MemberTypeInfo.Ignore)
continue;
//Member have a creator
else if ((kvp.Value & MemberTypeInfo.Creator) == MemberTypeInfo.Creator)
{
//MemberDelegate mDel = (MemberDelegate)_MembersCreators[this.TType][kvp.Key.Name];
Func<dynamic, object> mDel = (Func<dynamic, object>)_MembersCreators[this.TType][kvp.Key.Name];
if (kvp.Key.MemberType == MemberTypes.Property) ((PropertyInfo)kvp.Key).SetValue(mapped, mDel(dapperResult));
else ((FieldInfo)kvp.Key).SetValue(mapped, mDel(dapperResult));
}
//Member is IDictionary or IEnumerable
else if ((kvp.Value & MemberTypeInfo.IEnumerable) == MemberTypeInfo.IEnumerable)
{
Type t = GetMemberType(kvp.Key);
//if ((kvp.Value & MemberTypeInfo.Interface) == MemberTypeInfo.Interface) t = ResolveInterface(kvp.Key, dapperResult);
//else t = GetMemberType(kvp.Key);
/*
{
//Type of property or field
if (kvp.Key.MemberType == MemberTypes.Property) t = ((PropertyInfo)kvp.Key).PropertyType;
else t = ((FieldInfo)kvp.Key).FieldType;
}*/
bool isAnInterface = (kvp.Value & MemberTypeInfo.Interface) == MemberTypeInfo.Interface;
bool isNested = (kvp.Value & MemberTypeInfo.Nested) == MemberTypeInfo.Nested;
//If member is a dictionary
if (typeof(IDictionary).IsAssignableFrom(t))
{
//Create a dummy dictionary with the dapper's dynamic result which should be equal to the final one
DictionaryMapper dictMapper = new DictionaryMapper(dapperResult, kvp.Key.Name, isNested, isAnInterface, cleanResult, t, this);
try
{
if (kvp.Key.MemberType == MemberTypes.Property) ((PropertyInfo)kvp.Key).SetValue(mapped, dictMapper.DummyDictionary);
else ((FieldInfo)kvp.Key).SetValue(mapped, dictMapper.DummyDictionary);
}
catch (Exception err)
{
throw new CustomException_DapperMapper(
$#"DapperMapper.Map: Couldn't map IDictionary member {kvp.Key.Name} with value contained by dynamic object.
Incorrect type of value?: {kvp.Value.ToString()}",
err);
}
}
//Rest of enumerables
else
{
IEnumerable<dynamic> iEnumDapperResult;
//Select current member's values from dynamic
if (isNested && !cleanResult)
{
//Type mType = t; // GetMemberType(kvp.Key);//IEnumerable<T>
Type genericType = t.GenericTypeArguments[0];//mType.GenericTypeArguments[0];//T
if ((kvp.Value & MemberTypeInfo.Interface) == MemberTypeInfo.Interface)
{
bool genericIsInterfaceNotIEnumerable =
genericType.IsInterface &&
!typeof(IDictionary).IsAssignableFrom(genericType) &&
!(typeof(IEnumerable).IsAssignableFrom(genericType) && !typeof(string).IsAssignableFrom(genericType));
if (genericIsInterfaceNotIEnumerable) genericType = ResolveInterface(genericType, dapperResult);
}
iDapperMapper nestedMapper = MappersStore.GetMapper(genericType);
var nestedParser = new PrePostFixesParser(nestedMapper);
iEnumDapperResult = dapperResult
.Select(dyn => nestedParser.GetTypeMembersWithoutPrePostFixes(dyn, nestedMapper.NamesList));
}
else if (!cleanResult) iEnumDapperResult = dapperResult.Select(dyn => parser.RemovePrePostFixesFromDictionary(dyn));
else iEnumDapperResult = dapperResult;
//Create dummy IEnumerable
EnumerableMapper enumMapper = new EnumerableMapper(iEnumDapperResult, kvp.Key.Name, isNested, t, this.TType); ;
var dummy = Activator.CreateInstance(t, enumMapper.DummyEnumerable);
try
{
if (kvp.Key.MemberType == MemberTypes.Property) ((PropertyInfo)kvp.Key).SetValue(mapped, dummy);
else ((FieldInfo)kvp.Key).SetValue(mapped, dummy);
}
catch (Exception err)
{
throw new CustomException_DapperMapper(
$#"DapperMapper.Map: Couldn't map IEnumerable member {kvp.Key.Name} with value contained by dynamic object.
Incorrect type of value?: {kvp.Value.ToString()}",
err);
}
}
}//End IDictionary/IEnumerable
//If Built-in
else if ((kvp.Value & MemberTypeInfo.BuiltIn) == MemberTypeInfo.BuiltIn)
{
string name = parser.RemoveFieldsUnderscore(kvp.Key.Name);
IDictionary<string, object> dapperDict;
if (!cleanResult)
dapperDict = parser.GetTypeMembersWithoutPrePostFixes(dapperResult.First(), NamesList) as IDictionary<string, object>;
else
dapperDict = dapperResult.First() as IDictionary<string, object>;
if (!dapperDict.ContainsKey(name))
throw new CustomException_DapperMapper(
$#"DapperMapper.Map: There's no member in dynamic dapper result with name {kvp.Key.Name}. Cannot Map object.");
try
{
if (kvp.Key.MemberType == MemberTypes.Property) ((PropertyInfo)kvp.Key).SetValue(mapped, dapperDict[name]);
else ((FieldInfo)kvp.Key).SetValue(mapped, dapperDict[name]);
}
catch (Exception err)
{
throw new CustomException_DapperMapper(
$#"DapperMapper.Map: Couldn't map BuiltIn-type member {kvp.Key.Name} with value contained by dynamic object.
Incorrect type of value?: {kvp.Value.ToString()}",
err);
}
}
//if nested
else if ((kvp.Value & MemberTypeInfo.Nested) == MemberTypeInfo.Nested)
{
Type mType = GetMemberType(kvp.Key);
if ((kvp.Value & MemberTypeInfo.Interface) == MemberTypeInfo.Interface)
mType = ResolveInterface(mType, dapperResult);
//access generic Map method through nongeneric interface method
iDapperMapper nestedMapper = MappersStore.GetMapper(mType);
if (nestedMapper == null)
throw new CustomException_DapperMapper(
$#"DapperMapper.Map: No Mapper found at store for property {kvp.Key.Name} of type {mType.ToString()}.
If you want to map a nested property you have to create a mapper for that property type.");
if (kvp.Key.MemberType == MemberTypes.Property)
((PropertyInfo)kvp.Key).SetValue(mapped, nestedMapper.NoGenericMap(dapperResult, cleanResult));
else ((FieldInfo)kvp.Key).SetValue(mapped, nestedMapper.NoGenericMap(dapperResult, cleanResult));
}
}
return mapped;
}
/// <summary>
/// Generic map to IEnumerables. Result HAVE to be ordered by the splitOn column.
/// </summary>
/// <typeparam name="R"></typeparam>
/// <param name="dapperResult"></param>
/// <param name="splitOn"></param>
/// <param name="cleanResult"></param>
/// <returns></returns>
public R Map<R>(IEnumerable<dynamic> dapperResult, string splitOn = "Id", bool cleanResult = false)
where R : IEnumerable<T>
{
R result;
Type r = typeof(R);
List<dynamic> singleObjectDynamic = new List<dynamic>();
object splitObject = (dapperResult.First() as IDictionary<string, object>)[splitOn];
if (typeof(IList).IsAssignableFrom(r))
{
result = (R)Activator.CreateInstance(typeof(List<>).MakeGenericType(this.TType));
foreach (dynamic dyn in dapperResult)
{
IDictionary<string, object> dict = dyn as IDictionary<string, object>;
if (!dict.ContainsKey(splitOn))
throw new CustomException_DapperMapper(
$#"DapperMapper.Map(IEnumerable): Dapper result doesn't have a member with name equals to splitOn parameter.
SplitOn = {splitOn}");
if (!object.Equals(splitObject, dict[splitOn]) || dapperResult.Last() == dyn)
{
((IList)result).Add(Map(singleObjectDynamic));
singleObjectDynamic.Clear();
splitObject = dict[splitOn];
}
else
singleObjectDynamic.Add(dyn);
}
}
else
{
//http://stackoverflow.com/questions/18251587/assign-any-ienumerable-to-object-property
var addMethod = r.GetMethod("Add", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
// Property doesn't support Adding
if (addMethod == null)
throw new CustomException_DapperMapper(
$#"DapperMapper.Map(IEnumerable): Method Add doesn't exist in enumerable type to map.
enumType: {r.Name}.");
if (r.IsGenericTypeDefinition) result = (R)Activator.CreateInstance(r.MakeGenericType(this.TType));
else result = (R)Activator.CreateInstance(r);
foreach (dynamic dyn in dapperResult)
{
IDictionary<string, object> dict = dyn as IDictionary<string, object>;
if (!dict.ContainsKey(splitOn))
throw new CustomException_DapperMapper(
$#"DapperMapper.Map(IEnumerable): Dapper result doesn't have a member with name equals to splitOn parameter.
SplitOn = {splitOn}");
if (!object.Equals(splitObject, dict[splitOn]) || dapperResult.Last() == dyn)
{
addMethod.Invoke(result, new object[] { NoGenericMap(dyn) });
singleObjectDynamic.Clear();
splitObject = dict[splitOn];
}
else
singleObjectDynamic.Add(dyn);
}
}
return result;
}
/// <summary>
/// Non-generic Map.
/// </summary>
/// <param name="dapperResult"></param>
/// <returns></returns>
public object NoGenericMap(dynamic dapperResult, bool cleanResult = false)
{
IEnumerable<dynamic> ienum = new List<dynamic>() { dapperResult } as IEnumerable<dynamic>;
return this.Map(ienum, cleanResult);
}
/// <summary>
/// Non-generic Map.
/// </summary>
/// <param name="dapperResult"></param>
/// <param name="cleanResult"></param>
/// <returns></returns>
public object NoGenericMap(IEnumerable<dynamic> dapperResult, bool cleanResult = false)
{
return this.Map(dapperResult, cleanResult);
}
#endregion
}
QUESTION
So, what's happening?
I'm astonished by this one. Am I terribly wrong or class's methods should be able to see same class private methods?
For the DLL I just began it with a new Class Library project in visual studio 2015 community, and when done build it with the typical release configuration, then I went to a console project I'm using to testing it (I just made a simple database there, to have direct Dapper's dynamic results objects to test it) and added reference to the DLL. Could it be that I build the DLL wrong? Do I have to do something more in the testing project apart from referencing the DLL and add the corresponding using namespace?
I've tried searching for private methods, DLLs, private methods in DLLs, private methods in the public class in DLLs, but it seems I'm not able to find anything that solves it. Apart from checking the class and method have the proper access keywords (c# default access is private if no other is specified, right?), and clean, build, rebuild again both solutions, I don't see anymore.
I'm afraid I'm just forgetting something stupid, but it works if I copy-paste the code (or add it with "Add->Existing Item" as I said before") instead of using the DLL, therefore the code is correct, right?
EDIT:
Due to comment of GSerg I'm adding the way I call DapperMapper in the testing project after adding the reference to the DLL:
MapperStore store = new MapperStore();
DapperMapper<Persons> mapper = (DapperMapper<Persons>)store.GetMapper(typeof(Persons));
Persons p = mapper.Map(result);
EDIT2:
As can be seen in the comments, as suggested by GSerg I created a new console project for testing, add the reference to the DLL, copy-paste ONLY the test (mock classes, database), hit F5 and got the same result. I double checked that I builded the DLL and the test project at release mode, and use windows explorer to confirm that there were no DLL's .cs files at the testing project.

Entity Framework Add if not exist without update

I like the fact that AddOrUpdate let's you specify a filter to check to avoid adding duplicates. But I would like similar functionality without the update.
Right now I do something like this:
var checkProfile = from p in db.Profile
where p => p.LastName == newProfile.lastName
&& p => p.FirstName == newProfile.firstName
&& p => p.Middle== newProfile.middle
select p;
if (checkProfile.FirstOrDefault() == null)
{
db.Profile.Add(newProfile);
db.SaveChanges();
}
I know I can do something like this:
db.Profile.AddOrUpdate(p => new {p.LastName, p.FirstName, p.Middle}, newProfile);
db.SaveChanges();
But I would rather skip modifying the data in this case.
The first example does what I want but with more code. Is there a simpler/cleaner way to do what I want in the first example?
Update:
I like Ognyan Dimitrov's suggestion. I'm trying to implement it. My models inherit from BaseEntity. Can I put a generic version of that there?
My model is defined:
public class Address :BaseEntity
{
My BaseEntity:
public class BaseEntity
{
public virtual T AddIfNotExists<T>(T entity, Expression<Func<T, bool>> predicate = null)
{
var exists = predicate != null ? DbSet.Any(predicate) : DbSet.Any();
return !exists ? DbSet.Add(entity) : null;
}
}
I'm getting errors for Any(...) and Add(...). The error for Add(...) is 'An object reference is required for the non-static field, method, or property 'System.Data.Entity.DbSet.Add(object)' '
Should I be using this.Add(object) ?
Update 2:
I've created this code:
public static class DbSetExtensions
{
public static T AddIfNotExists<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate = null) where T : class, new()
{
var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any();
return !exists ? dbSet.Add(entity) : null;
}
}
Now I'm trying to call it like this, but it's not correct. Forgive my lack of understanding.
_db.ProfileIdentifier.AddIfNotExists(newIdentifier,
pi => new {pi.ProfileId, pi.ProfileIdentifierTypeId, pi.ProfileIdentifierValue});
Update - Solution:
I can call the DbSetextensions like this:
_db.ProfileIdentifier.AddIfNotExists(newIdentifier,
pi => pi.ProfileId == profileId &&
pi.ProfileIdentifierTypeId == (int)type &&
pi.ProfileIdentifierValue == value);
Thanks a lot for working with me Ognyan!!!
Have you tried to check if the entity exists and if not - add it? Like this :
UPDATE
using System.Linq.Expressions;
public class ContextWithExtensionExample
{
public void DoSomeContextWork(DbContext context)
{
var uni = new Unicorn();
context.Set<Unicorn>().AddIfNotExists(uni, x => x.Name == "James");
}
}
public static class DbSetExtensions
{
public static T AddIfNotExists<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate = null) where T : class, new()
{
var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any();
return !exists ? dbSet.Add(entity) : null;
}
}
You can use this method directly and remember to call DbContext.SaveChanges() after the call.
All the other answers are incorrect.
"Read before write" can violate data integrity without being put inside a transaction control.
In SQL Server, you can use merge statement. However merge statement is not available in EF.
The solution is OK, when you have to add just one item, but it's very expensive in terms of performance in case you have to add multiple items.
I think there is a better solution:
public static class DbSetExtensions
{
public static EntityEntry<TEnt> AddIfNotExists<TEnt, TKey>(this DbSet<TEnt> dbSet, TEnt entity, Func<TEnt, TKey> predicate) where TEnt : class
{
var exists = dbSet.Any(c => predicate(entity).Equals(predicate(c)));
return exists
? null
: dbSet.Add(entity);
}
public static void AddRangeIfNotExists<TEnt, TKey>(this DbSet<TEnt> dbSet, IEnumerable<TEnt> entities, Func<TEnt, TKey> predicate) where TEnt : class
{
var entitiesExist = from ent in dbSet
where entities.Any(add => predicate(ent).Equals(predicate(add)))
select ent;
dbSet.AddRange(entities.Except(entitiesExist));
}
}
So later it can be used like this:
using (var context = new MyDbContext())
{
var user1 = new User { Name = "Peter", Age = 32 };
context.Users.AddIfNotExists(user1, u => u.Name);
var user2 = new User { Name = "Joe", Age = 25 };
context.Users.AddIfNotExists(user2, u => u.Age);
// Adds user1 if there is no user with name "Peter"
// Adds user2 if there is no user with age 25
context.SaveChanges();
}
I used something like, read these two posts to make my code.
I hope to help those in need of a similar signature to AddOrUpdate.
Entity Framework Add if not exist without update
Making AddOrUpdate change only some properties
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace System.Data.Entity.Migrations
{
//
// Summary:
// Metodos de extensão para System.Data.Entity.IDbSet
public static class DbSetMigrationsGustavoExtensions
{
/// <summary>
/// Adiciona uma entidade se ela não existe ainda
/// Assinatura semelhante ao AddOrUpdate
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="set">Set onde serão adicionadas as entidades</param>
/// <param name="identifierExpression">Campos usados na comparação</param>
/// <param name="entities">Entidades para adicionar</param>
public static void AddIfNotExists<TEntity>(this IDbSet<TEntity> set, Expression<Func<TEntity, object>> identifierExpression, params TEntity[] entities) where TEntity : class
{
var identifyingProperties = GetProperties<TEntity>(identifierExpression).ToList();
var parameter = Expression.Parameter(typeof(TEntity));
foreach (var entity in entities)
{
var matches = identifyingProperties.Select(pi => Expression.Equal(Expression.Property(parameter, pi.Name), Expression.Constant(pi.GetValue(entity, null))));
var matchExpression = matches.Aggregate<BinaryExpression, Expression>(null, (agg, v) => (agg == null) ? v : Expression.AndAlso(agg, v));
var predicate = Expression.Lambda<Func<TEntity, bool>>(matchExpression, new[] { parameter });
if (!set.Any(predicate))
{
set.Add(entity);
}
}
}
private static IEnumerable<PropertyInfo> GetProperties<T>(Expression<Func<T, object>> exp) where T : class
{
Debug.Assert(exp != null);
Debug.Assert(exp.Body != null);
Debug.Assert(exp.Parameters.Count == 1);
var type = typeof(T);
var properties = new List<PropertyInfo>();
if (exp.Body.NodeType == ExpressionType.MemberAccess)
{
var memExp = exp.Body as MemberExpression;
if (memExp != null && memExp.Member != null)
properties.Add(type.GetProperty(memExp.Member.Name));
}
else if (exp.Body.NodeType == ExpressionType.Convert)
{
var unaryExp = exp.Body as UnaryExpression;
if (unaryExp != null)
{
var propExp = unaryExp.Operand as MemberExpression;
if (propExp != null && propExp.Member != null)
properties.Add(type.GetProperty(propExp.Member.Name));
}
}
else if (exp.Body.NodeType == ExpressionType.New)
{
var newExp = exp.Body as NewExpression;
if (newExp != null)
properties.AddRange(newExp.Members.Select(x => type.GetProperty(x.Name)));
}
return properties.OfType<PropertyInfo>();
}
/// <summary>
/// Faz um set.Any(predicate)
/// Se não existe nada no set então adiciona
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="set">Set onde será adicionada a entidade</param>
/// <param name="predicate">Condição (exemplo: dbUser => dbUser.Nome == "Gustavo")</param>
/// <param name="entity">Entidade para adicionar</param>
/// <returns></returns>
public static T AddIfNotExists<T>(this IDbSet<T> set, Expression<Func<T, bool>> predicate, T entity) where T : class, new()
{
return !set.Any(predicate) ? set.Add(entity) : null;
}
}
}
Quoted from MSDN EF article.
Insert or update pattern
A common pattern for some applications is to either Add an entity as
new (resulting in a database insert) or Attach an entity as existing
and mark it as modified (resulting in a database update) depending on
the value of the primary key. For example, when using database
generated integer primary keys it is common to treat an entity with a
zero key as new and an entity with a non-zero key as existing. This
pattern can be achieved by setting the entity state based on a check
of the primary key value.
Note that when you change the state to Modified all the properties of
the entity will be marked as modified and all the property values will
be sent to the database when SaveChanges is called.
context.Entry(profile).State = profile.Id == 0 ? EntityState.Added : EntityState.Modified;
context.SaveChanges();
What worked for me is this:
public static void AddIfNotExists<T>(this DbSet<T> dbSet, Func<T, object> predicate, params T [] entities) where T : class, new()
{
foreach (var entity in entities)
{
var newValues = predicate.Invoke(entity);
Expression<Func<T, bool>> compare = arg => predicate(arg).Equals(newValues);
var compiled = compare.Compile();
var existing = dbSet.FirstOrDefault(compiled);
if (existing == null)
{
dbSet.Add(entity);
}
}
}
The only thing that comes to mind is to use IEqualityComparer<T>, but this doesn't really stop the work, merely abstracts it away and creates cleaner code.

IQueryable failback to IEnumerable

Having IQueryable<T> returned from Linq-2-SQL data context is it possible to wrap it with custom IQueryable<T> implementation that on execution stage (for instance, while enumeration) will handle underlying IQueryProvider's NotSupportedException and failback to enumerating whole data context and applying Expression using in-memory IQueryProvider (System.Linq.EnumerableQuery<T>)?
Code example:
IQueryable<User> users = usersRepository.GetAll();
// linq-to-sql can handle it
users.Where(u => u.Id > 10).ToList();
// linq-to-sql IQueryProvider will throw NotSupportedException in run-time,
// because ComplexFilter body was not captured as Expression
users.Where(u => ComplexFilter(u)).ToList();
// will succeed, because EnumerableQuery do not need Expression Tree
users.AsEnumerable().Where(u => ComplexFilter(u)).ToList();
public static bool ComplexFilter(User user)
{
return user.Id + user.Name.Length > 12;
}
I looking for way to wrap IQueryable that will automatically failback to IEnumerable if underlying IQueryProvider throws NotSupportedException:
IQueryable<User> safeUsersProxy = new SafeUsersProxy<User>(users);
// I want it to try underlying linq-to-sql provider first
// and then automatically failback to IEnumerable and return
// the result anyway (do not care about whole data enumerating in such cases)
safeUsersProxy.Where(u => ComplexFilter(u)).ToList();
The point
This will allow convenient way to execute all queries on DB level whenever possible, and only if Linq-to-SQL was unable to convert Expression into the SQL use slow EnumerableQuery fetching the whole dataset.
Now, I always thought that if someone asks you some rope to hang himself you should only ask two questions, "are you sure?" and "how much rope do you need?". So you ask me for some rope, you told us you are sure you want this rope, so who am I to not give you this rope?
This is what you asked... Let's call it a v0.5 . It even has a codename: Armed and Dangerous. I've even given to v0.1 a codename: Running with Scissors. It is disponible at http://pastebin.com/6qLs8TPt . (the v0.2 is Lost in Space, because I've forgot it on another computer :-) ). I've added to the v0.3 Playing with Fire http://pastebin.com/pRbKt1Z2 a simple logger and a Stack Trace augmenter, and to the v0.4 Crossing without Looking http://pastebin.com/yEhc9vjg a little EF compatibility.
I checked it with simple queries and Join(s). It seems to work. Much could be added. For example an "intelligent" projector that analyzes the Select that probably is removed from the query and tries to rebuild it... But this can be a good start. There is a small problem: as I've written in a comment, depending on the presence/not presence of a Select, the LINQ-to-SQL loads the returned objects in its Object Tracker. If in the "original" query there is a Select (so no object tracking), and I remove it to execute it locally, then the full object will be loaded, and the object tracking will be turned "on". Clearly you can context.ObjectTrackingEnabled = false.
Now it should be compatible with simple EF queries. Not compatible with AsNoTracking()/Include().
The NotSupportedException is tracked in an Exception property. Note that this property is modified only in the most "external" IQueryable. So
// Optional :-) You can ignore the logger and live happy...
// Not optional: you can live happy very far from me!
SafeQueryable.Logger = (iqueriable, expression, e) => Console.WriteLine(e);
var q1 = context.Items.AsSafe();
var q2 = q1.Where(x => x.ID.GetHashCode() > 0);
var q3 = q2.Select(x => x.ID);
var ex = ((ISafeQueryable)q3).Exception;
How to use it? There is a single extension method, AsSafe(). You use it on a IQueryable<T> and then you execute the query... And live happy... Playing with Fire! :-)
Examples of use:
// Queries that are NotSupportedException with LINQ-to-SQL
var q1 = context.Items.AsSafe().Where(x => x.ID.GetHashCode() > 0).Take(2).Select(x => x.ID);
var q2 = context.Items.Where(x => x.ID.GetHashCode() > 0).AsSafe().Take(2).Select(x => x.ID);
var q3 = context.Items.Where(x => x.ID.GetHashCode() > 0).Take(2).AsSafe().Select(x => x.ID);
var q4 = context.Items.Where(x => x.ID.GetHashCode() > 0).Take(2).Select(x => x.ID).AsSafe();
//// Queries that are OK with LINQ-to-SQL
//var q1 = context.Items.AsSafe().Where(x => x.ID > 0).Take(2).Select(x => x.ID);
//var q2 = context.Items.Where(x => x.ID > 0).AsSafe().Take(2).Select(x => x.ID);
//var q3 = context.Items.Where(x => x.ID > 0).Take(2).AsSafe().Select(x => x.ID);
//var q4 = context.Items.Where(x => x.ID > 0).Take(2).Select(x => x.ID).AsSafe();
var r1 = q1.ToList();
var r2 = q2.First();
var r3 = q3.Max();
// The Aggregate isn't normally supported by LINQ-to-SQL
var r4 = q4.Aggregate((x, y) => x + y);
var ex1 = ((ISafeQueryable)q1).Exception;
var ex2 = ((ISafeQueryable)q2).Exception;
var ex3 = ((ISafeQueryable)q3).Exception;
var ex4 = ((ISafeQueryable)q4).Exception;
As you can see, you can use the AsSafe() at any step, and it will work. This happens because the query is wrapped by AsSafe() in a SafeQueryable<T> that is both a IQueryProvider and a IQueryable<T> (like the classes of LINQ-to-SQL). In this way every other IQueryable method you call on it will produce other SafeQueryable<T> objects (so it self-reproduce :-) )
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
/// <summary>
/// v0.5 Codename: Armed and Dangerous
///
/// (previous version v0.1 Codename: Running with Scissors: http://pastebin.com/6qLs8TPt)
/// (previous version v0.2 Codename: Lost in Space: lost in space :-) )
/// (previous version v0.3 Codename: Playing with Fire: http://pastebin.com/pRbKt1Z2)
/// (previous version v0.4 Codename: Crossing without Looking: http://pastebin.com/yEhc9vjg)
///
/// Support class with an extension method to make "Safe" an IQueryable
/// or IQueryable<T>. Safe as "I work for another company, thousand
/// of miles from you. I do hope I won't ever buy/need something from
/// your company".
/// The Extension methods wraps a IQueryable in a wrapper that then can
/// be used to execute a query. If the original Provider doesn't suppport
/// some methods, the query will be partially executed by the Provider
/// and partially executed locally.
///
/// Minimal support for EF.
///
/// Note that this **won't** play nice with the Object Tracking!
///
/// Not suitable for programmers under 5 years (of experience)!
/// Dangerous if inhaled or executed.
/// </summary>
public static class SafeQueryable
{
/// <summary>
/// Optional logger to log the queries that are "corrected. Note that
/// there is no "strong guarantee" that the IQueriable (that is also
/// an IQueryProvider) is executing its (as in IQueriable.Expression)
/// Expression, so an explicit Expression parameter is passed. This
/// because the IQueryProvider.Execute method receives an explicit
/// expression parameter. Clearly there is a "weak guarantee" that
/// unless you do "strange things" this won't happen :-)
/// </summary>
public static Action<IQueryable, Expression, NotSupportedException> Logger { get; set; }
/// <summary>
/// Return a "Safe" IQueryable<T>.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static IQueryable<T> AsSafe<T>(this IQueryable<T> source)
{
if (source is SafeQueryable<T>)
{
return source;
}
return new SafeQueryable<T>(source);
}
}
/// <summary>
/// Simple interface useful to collect the Exception, or to recognize
/// a SafeQueryable<T>.
/// </summary>
public interface ISafeQueryable
{
NotSupportedException Exception { get; }
}
/// <summary>
/// "Safe" wrapper around a IQueryable<T;>
/// </summary>
/// <typeparam name="T"></typeparam>
public class SafeQueryable<T> : IOrderedQueryable<T>, IQueryProvider, ISafeQueryable
{
protected static readonly FieldInfo StackTraceStringField = typeof(Exception).GetField("_stackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
// The query. Note that it can be "transformed" to a "safe" version
// of itself. When it happens, IsSafe becomes true
public IQueryable<T> Query { get; protected set; }
// IsSafe means that the query has been "corrected" if necessary and
// won't throw a NotSupportedException
protected bool IsSafe { get; set; }
// Logging of the "main" NotSupportedException.
public NotSupportedException Exception { get; protected set; }
public SafeQueryable(IQueryable<T> query)
{
Query = query;
}
/* IQueryable<T> */
public IEnumerator<T> GetEnumerator()
{
if (IsSafe)
{
return Query.GetEnumerator();
}
return new SafeEnumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public Type ElementType
{
get { return Query.ElementType; }
}
public Expression Expression
{
get { return Query.Expression; }
}
public IQueryProvider Provider
{
get { return this; }
}
/* IQueryProvider */
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return CreateQueryImpl<TElement>(expression);
}
public IQueryable CreateQuery(Expression expression)
{
Type iqueryableArgument = GetIQueryableTypeArgument(expression.Type);
MethodInfo createQueryImplMethod = typeof(SafeQueryable<T>)
.GetMethod("CreateQueryImpl", BindingFlags.Instance | BindingFlags.NonPublic)
.MakeGenericMethod(iqueryableArgument);
return (IQueryable)createQueryImplMethod.Invoke(this, new[] { expression });
}
public TResult Execute<TResult>(Expression expression)
{
return ExecuteImpl<TResult>(expression);
}
public object Execute(Expression expression)
{
Type iqueryableArgument = GetIQueryableTypeArgument(expression.Type);
MethodInfo executeImplMethod = typeof(SafeQueryable<T>)
.GetMethod("ExecuteImpl", BindingFlags.Instance | BindingFlags.NonPublic)
.MakeGenericMethod(iqueryableArgument);
return executeImplMethod.Invoke(this, new[] { expression });
}
/* Implementation methods */
// Gets the T of IQueryablelt;T>
protected static Type GetIQueryableTypeArgument(Type type)
{
IEnumerable<Type> interfaces = type.IsInterface ?
new[] { type }.Concat(type.GetInterfaces()) :
type.GetInterfaces();
Type argument = (from x in interfaces
where x.IsGenericType
let gt = x.GetGenericTypeDefinition()
where gt == typeof(IQueryable<>)
select x.GetGenericArguments()[0]).FirstOrDefault();
return argument;
}
protected IQueryable<TElement> CreateQueryImpl<TElement>(Expression expression)
{
return new SafeQueryable<TElement>(Query.Provider.CreateQuery<TElement>(expression));
}
protected TResult ExecuteImpl<TResult>(Expression expression)
{
if (IsSafe && Query.Expression == expression)
{
TResult result = Query.Provider.Execute<TResult>(expression);
return result;
}
try
{
// Note that thanks to how everything knits together, if you
// call query1.First(); query1.First(); the second call will
// get to use the query cached by the first one (technically
// the cached query will be only the "query1" part)
// We try executing it directly
TResult result = Query.Provider.Execute<TResult>(expression);
// Success!
if (!IsSafe && CanCache(expression, true))
{
IsSafe = true;
}
return result;
}
catch (NotSupportedException e1)
{
// Clearly there was a NotSupportedException :-)
Tuple<IEnumerator<T>, bool, TResult> result = HandleEnumerationFailure<TResult>(e1, expression, true);
if (result == null)
{
throw;
}
// Success!
return result.Item3;
}
}
// Is used both indirectly by GetEnumerator() and by Execute<>.
// The returned Tuple<,,> has the first two elements that are valid
// when used by the GetEnumerator() and the last that is valid
// when used by Execute<>.
protected Tuple<IEnumerator<T>, bool, TResult> HandleEnumerationFailure<TResult>(NotSupportedException e1, Expression expression, bool singleResult)
{
// We "augment" the exception with the full stack trace
AugmentStackTrace(e1, 3);
if (SafeQueryable.Logger != null)
{
SafeQueryable.Logger(this, expression, e1);
}
// We save this first exception
Exception = e1;
{
var query = Query;
MethodInfo executeSplittedMethod = typeof(SafeQueryable<T>).GetMethod("ExecuteSplitted", BindingFlags.Instance | BindingFlags.NonPublic);
MethodCallExpression call;
Expression innerExpression = expression;
Type iqueryableArgument;
// We want to check that there is a MethodCallExpression with
// at least one argument, and that argument is an Expression
// of type IQueryable<iqueryableArgument>, and we save the
// iqueryableArgument
while ((call = innerExpression as MethodCallExpression) != null &&
call.Arguments.Count > 0 &&
(innerExpression = call.Arguments[0] as Expression) != null &&
(iqueryableArgument = GetIQueryableTypeArgument(innerExpression.Type)) != null)
{
try
{
Tuple<IEnumerator<T>, bool, TResult> result2 = (Tuple<IEnumerator<T>, bool, TResult>)executeSplittedMethod.MakeGenericMethod(iqueryableArgument, typeof(TResult)).Invoke(this, new object[] { expression, call, innerExpression, singleResult });
return result2;
}
catch (TargetInvocationException e2)
{
if (!(e2.InnerException is NotSupportedException))
{
throw;
}
}
}
return null;
}
}
// Is used both indirectly by GetEnumerator() and by Execute<>.
// The returned Tuple<,,> has the first two elements that are valid
// when used by the GetEnumerator() and the last that is valid
// when used by Execute<>.
protected Tuple<IEnumerator<T>, bool, TResult> ExecuteSplitted<TInner, TResult>(Expression expression, MethodCallExpression call, Expression innerExpression, bool singleResult)
{
// The NotSupportedException should happen here
IQueryable<TInner> innerQueryable = Query.Provider.CreateQuery<TInner>(innerExpression);
// We try executing it directly
IEnumerator<TInner> innerEnumerator = innerQueryable.GetEnumerator();
bool moveNextSuccess = innerEnumerator.MoveNext();
IEnumerator<T> enumerator;
TResult singleResultValue;
// Success!
{
// Now we wrap the partially used enumerator in an
// EnumerableFromStartedEnumerator
IEnumerable<TInner> innerEnumerable = new EnumerableFromStartedEnumerator<TInner>(innerEnumerator, moveNextSuccess, innerQueryable);
// Then we apply an AsQueryable, that does some magic
// to make the query appear to be a Queryable
IQueryable<TInner> innerEnumerableAsQueryable = Queryable.AsQueryable(innerEnumerable);
// We rebuild a new expression by changing the "old"
// inner parameter of the MethodCallExpression with the
// queryable we just built
var arguments = call.Arguments.ToArray();
arguments[0] = Expression.Constant(innerEnumerableAsQueryable);
MethodCallExpression call2 = Expression.Call(call.Object, call.Method, arguments);
Expression expressionWithFake = new SimpleExpressionReplacer(call, call2).Visit(expression);
// We "execute" locally the whole query through a second
// "outer" instance of the EnumerableQuery (this class is
// the class that "implements" the "fake-magic" of
// AsQueryable)
IQueryable<T> queryable = new EnumerableQuery<T>(expressionWithFake);
if (singleResult)
{
enumerator = null;
moveNextSuccess = false;
singleResultValue = queryable.Provider.Execute<TResult>(queryable.Expression);
}
else
{
enumerator = queryable.GetEnumerator();
moveNextSuccess = enumerator.MoveNext();
singleResultValue = default(TResult);
}
}
// We could enter here with a new query from Execute<>(),
// with IsSafe == true . It would be useless to try to cache
// that query.
if (!IsSafe && CanCache(expression, singleResult))
{
Stopwatch sw = Stopwatch.StartNew();
// We redo the same things to create a second copy of
// the query that is "complete", not partially
// enumerated. This second copy will be cached in the
// SafeQueryable<T>.
// Note that forcing the Queryable.AsQueryable to not
// "recast" the query to the original IQueryable<T> is
// quite complex :-) We have to
// .AsEnumerable().Select(x => x) .
IEnumerable<TInner> innerEnumerable = innerQueryable.AsEnumerable().Select(x => x);
IQueryable<TInner> innerEnumerableAsQueryable = Queryable.AsQueryable(innerEnumerable);
// Note that we cache the SafeQueryable<>.Expression!
var arguments = call.Arguments.ToArray();
arguments[0] = Expression.Constant(innerEnumerableAsQueryable);
MethodCallExpression call2 = Expression.Call(call.Object, call.Method, arguments);
Expression expressionWithFake = new SimpleExpressionReplacer(call, call2).Visit(Expression);
IQueryable<T> queryable = new EnumerableQuery<T>(expressionWithFake);
// Now the SafeQueryable<T> has a query that *just works*
Query = queryable;
IsSafe = true;
sw.Stop();
Console.WriteLine(sw.ElapsedTicks);
}
return Tuple.Create(enumerator, moveNextSuccess, singleResultValue);
}
protected bool CanCache(Expression expression, bool singleResult)
{
// GetEnumerator() doesn't permit changing the query
if (!singleResult)
{
return true;
}
// The expression is equal to the one in Query.Expression
// (should be very rare!)
if (Query.Expression == expression)
{
return true;
}
MethodCallExpression call;
Expression innerExpression = expression;
Type iqueryableArgument;
// We walk back the expression to see if a smaller part of it is
// the "original" Query.Expression . This happens for example
// when one of the operators that returns a single value
// (.First(), .FirstOrDefault(), .Single(), .SingleOrDefault(),
// .Any(), .All()., .Min(), .Max(), ...) are used.
while ((call = innerExpression as MethodCallExpression) != null &&
call.Arguments.Count > 0 &&
(innerExpression = call.Arguments[0] as Expression) != null &&
(iqueryableArgument = GetIQueryableTypeArgument(innerExpression.Type)) != null)
{
if (Query.Expression == innerExpression)
{
return true;
}
}
return false;
}
// The StackTrace of an Exception "stops" at the catch. This method
// "augments" it to include the full stack trace.
protected static void AugmentStackTrace(Exception e, int skipFrames = 2)
{
// Playing with a private field here. Don't do it at home :-)
// If not present, do nothing.
if (StackTraceStringField == null)
{
return;
}
string stack1 = e.StackTrace;
string stack2 = new StackTrace(skipFrames, true).ToString();
string stack3 = stack1 + stack2;
StackTraceStringField.SetValue(e, stack3);
}
/* Utility classes */
// An IEnumerator<T> that applies the AsSafe() paradigm, knowing that
// normally the exception happens only on the first MoveFirst().
protected class SafeEnumerator : IEnumerator<T>
{
protected readonly SafeQueryable<T> SafeQueryable_;
protected IEnumerator<T> Enumerator { get; set; }
public SafeEnumerator(SafeQueryable<T> safeQueryable)
{
SafeQueryable_ = safeQueryable;
}
public T Current
{
get
{
return Enumerator != null ? Enumerator.Current : default(T);
}
}
public void Dispose()
{
if (Enumerator != null)
{
Enumerator.Dispose();
}
}
object IEnumerator.Current
{
get { return Current; }
}
public bool MoveNext()
{
// We handle exceptions only on first MoveNext()
if (Enumerator != null)
{
return Enumerator.MoveNext();
}
try
{
// We try executing it directly
Enumerator = SafeQueryable_.Query.GetEnumerator();
bool result = Enumerator.MoveNext();
// Success!
SafeQueryable_.IsSafe = true;
return result;
}
catch (NotSupportedException e1)
{
// Clearly there was a NotSupportedException :-)
Tuple<IEnumerator<T>, bool, T> result = SafeQueryable_.HandleEnumerationFailure<T>(e1, SafeQueryable_.Query.Expression, false);
if (result == null)
{
throw;
}
Enumerator = result.Item1;
return result.Item2;
}
}
public void Reset()
{
if (Enumerator != null)
{
Enumerator.Reset();
}
}
}
}
// A simple expression visitor to replace some nodes of an expression
// with some other nodes
public class SimpleExpressionReplacer : ExpressionVisitor
{
public readonly Dictionary<Expression, Expression> Replaces;
public SimpleExpressionReplacer(Dictionary<Expression, Expression> replaces)
{
Replaces = replaces;
}
public SimpleExpressionReplacer(IEnumerable<Expression> from, IEnumerable<Expression> to)
{
Replaces = new Dictionary<Expression, Expression>();
using (var enu1 = from.GetEnumerator())
using (var enu2 = to.GetEnumerator())
{
while (true)
{
bool res1 = enu1.MoveNext();
bool res2 = enu2.MoveNext();
if (!res1 || !res2)
{
if (!res1 && !res2)
{
break;
}
if (!res1)
{
throw new ArgumentException("from shorter");
}
throw new ArgumentException("to shorter");
}
Replaces.Add(enu1.Current, enu2.Current);
}
}
}
public SimpleExpressionReplacer(Expression from, Expression to)
{
Replaces = new Dictionary<Expression, Expression> { { from, to } };
}
public override Expression Visit(Expression node)
{
Expression to;
if (node != null && Replaces.TryGetValue(node, out to))
{
return base.Visit(to);
}
return base.Visit(node);
}
}
// Simple IEnumerable<T> that "uses" an IEnumerator<T> that has
// already received a MoveNext(). "eats" the first MoveNext()
// received, then continues normally. For shortness, both IEnumerable<T>
// and IEnumerator<T> are implemented by the same class. Note that if a
// second call to GetEnumerator() is done, the "real" IEnumerator<T> will
// be returned, not this proxy implementation.
public class EnumerableFromStartedEnumerator<T> : IEnumerable<T>, IEnumerator<T>
{
public readonly IEnumerator<T> Enumerator;
public readonly IEnumerable<T> Enumerable;
// Received by creator. Return value of MoveNext() done by caller
protected bool FirstMoveNextSuccessful { get; set; }
// The Enumerator can be "used" only once, then a new enumerator
// can be requested by Enumerable.GetEnumerator()
// (default = false)
protected bool Used { get; set; }
// The first MoveNext() has been already done (default = false)
protected bool DoneMoveNext { get; set; }
public EnumerableFromStartedEnumerator(IEnumerator<T> enumerator, bool firstMoveNextSuccessful, IEnumerable<T> enumerable)
{
Enumerator = enumerator;
FirstMoveNextSuccessful = firstMoveNextSuccessful;
Enumerable = enumerable;
}
public IEnumerator<T> GetEnumerator()
{
if (Used)
{
return Enumerable.GetEnumerator();
}
Used = true;
return this;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public T Current
{
get
{
// There are various school of though on what should
// happens if called before the first MoveNext() or
// after a MoveNext() returns false. We follow the
// "return default(TInner)" school of thought for the
// before first MoveNext() and the "whatever the
// Enumerator wants" for the after a MoveNext() returns
// false
if (!DoneMoveNext)
{
return default(T);
}
return Enumerator.Current;
}
}
public void Dispose()
{
Enumerator.Dispose();
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public bool MoveNext()
{
if (!DoneMoveNext)
{
DoneMoveNext = true;
return FirstMoveNextSuccessful;
}
return Enumerator.MoveNext();
}
public void Reset()
{
// This will 99% throw :-) Not our problem.
Enumerator.Reset();
// So it is improbable we will arrive here
DoneMoveNext = true;
}
}
Note that there is a bug in the LINQ-to-SQL:
var q5 = context.Items.Where(x => x.ID > context.Nodis.Min(y => y.ID.GetHashCode()));
var r5 = q5.ToList();
This won't throw NotSupportedException but won't execute correctly. I think it could be a problem with many queries that use the context.SomeTable "internally" in the query.
If I'm not mistaken, you need to materialize the collection through .ToList () before using your method. Try to convert the collection into a list and store it in a variable. After that, try to use his method in the collection that was generated in this variable.
I don't think there's an automatic way to do this, other than writing your own ExtressionTree
But analyzing your code I think that what you need is to use an Extension Method to encapsulate your query
Something like this (I already tested the concept using LinqToSql and it supports the Length property):
public static class Extensions
{
public static IQueryable<User> WhereUserMatches(this IQueryable<User> source)
{
return source.Where(x => x.Id + x.Name.Length > 12);
}
}
Usage
var myFileterdUsers = users.WhereUserMatches();
And since you are working with IQueryable then your condition will be sent to the server not in memory
With this approach you can encapsulate your complex queries and actually execute them in the server instead of memory.
A word about hiding the Exception
If the query cannot be handled by the provider I wouldn't recommend you to hide the exception by falling back to use IEnumerable (in memory query) because this could cause undesired behavior in your application
I'd recommend you to be explicit and throw an exception if the underlying query provider cannot convert it to a valid ExpressionTree
Exceptions are good, they are our friends, they let us know if we are doing something wrong
Hiding the exception is the opposite and it's considered a bad practice, hiding the exception will give you the ilusion that your application works even when most likely it won't do exactly what you think it does

When using a repository is it possible for a type to return a Func that the repository uses to test for existing entities?

For example given a Factory with a method
public static T Save<T>(T item) where T : Base, new()
{
/* item.Id == Guid.Empty therefore item is new */
if (item.Id == Guid.Empty && repository.GetAll<T>(t => t.Name == item.Name))
{
throw new Exception("Name is not unique");
}
}
how do I create a property of Base (say MustNotAlreadyExist) so that I can change the method above to
public static T Save<T>(T item) where T : Base, new()
{
/* item.Id == Guid.Empty therefore item is new */
if (item.Id == Guid.Empty && repository.GetAll<T>(t.MustNotAlreadyExist))
{
throw new Exception("Name is not unique");
}
}
public class Base
{
...
public virtual Expression<Func<T, bool>> MustNotAlreadyExist()
{
return (b => b.Name == name); /* <- this clearly doesn't work */
}
}
and then how can I override MustNotAlreadyExist in Account : Base
public class Account : Base
{
...
public override Expression<Func<T, bool>> MustNotAlreadyExist()
{
return (b => b.Name == name && b.AccountCode == accountCode); /* <- this doesn't work */
}
...
}
Try this:
public class Account : Base
{
...
public override Expression<Func<T, bool>> MustNotAlreadyExist()
{
return (b => b.Name == name && b.AccountCode == accountCode).Any();
}
...
}
The Any() method will return true if any record matches the predicate. It could be argued that it is outside the responsibility of the repository to check for presence of a record before saving.
UPDATE:
There is a great article on CodeProject that describes a generic Repository for Entity Framework:
http://www.codeproject.com/KB/database/ImplRepositoryPatternEF.aspx
This could be applied to a non-Entity Framework data context. Here is an excerpt that provides a very flexible method for checking for an existing value by accepting the name of a field, a value, and a key value. You can apply this to any Entity type and use it to check for the presence of an entity before attempting a save.
/// <summary>
/// Check if value of specific field is already exist
/// </summary>
/// <typeparam name="E"></typeparam>
/// <param name="fieldName">name of the Field</param>
/// <param name="fieldValue">Field value</param>
/// <param name="key">Primary key value</param>
/// <returns>True or False</returns>
public bool TrySameValueExist(string fieldName, object fieldValue, string key)
{
// First we define the parameter that we are going to use the clause.
var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
MemberExpression leftExprFieldCheck =
MemberExpression.Property(xParam, fieldName);
Expression rightExprFieldCheck = Expression.Constant(fieldValue);
BinaryExpression binaryExprFieldCheck =
MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
MemberExpression leftExprKeyCheck =
MemberExpression.Property(xParam, this._KeyProperty);
Expression rightExprKeyCheck = Expression.Constant(key);
BinaryExpression binaryExprKeyCheck =
MemberExpression.NotEqual(leftExprKeyCheck, rightExprKeyCheck);
BinaryExpression finalBinaryExpr =
Expression.And(binaryExprFieldCheck, binaryExprKeyCheck);
//Create Lambda Expression for the selection
Expression<Func<E, bool>> lambdaExpr =
Expression.Lambda<Func<E, bool>>(finalBinaryExpr,
new ParameterExpression[] { xParam });
//Searching ....
return ((IRepository<E, C>)this).TryEntity(new Specification<E>(lambdaExpr));
}
/// <summary>
/// Check if Entities exist with Condition
/// </summary>
/// <param name="selectExpression">Selection Condition</param>
/// <returns>True or False</returns>
public bool TryEntity(ISpecification<E> selectSpec)
{
return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]").Any<E>
(selectSpec.EvalPredicate);
}
I am not shure if you problem is solvable since you need to access both the repository and the new item to be checked. The new item to be checked is not available in a seperate method.
However you can outsource the call to GetAll so that your code becomes something similar to
(not tested)
public static T Save<T>(T item) where T : Base, new()
{
if (item.Id == Guid.Empty && (Check(repository, item)))
{
throw new Exception("Name is not unique");
}
}
public class Base
{
...
public Func<Enumerable<T>, T, bool> Check { get; set;}
public Base()
{
Check = (col, newItem) => (null != col.FirstOrDefault<T>(
item => item.Name == newItem.Name));
}
}
OK, here is the answer, this is a combination of the code posted by Dave Swersky and a little but of common sense.
public interface IUniqueable<T>
{
Expression<Func<T, bool>> Unique { get; }
}
public class Base, IUniqueable<Base>
{
...
public Expression<Func<Base, bool>> Unique
{
get
{
var xParam = Expression.Parameter(typeof(Base), typeof(Base).Name);
MemberExpression leftExprFieldCheck = MemberExpression.Property(xParam, "Name");
Expression rightExprFieldCheck = Expression.Constant(this.Name);
BinaryExpression binaryExprFieldCheck = MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
return Expression.Lambda<Func<Base, bool>>(binaryExprFieldCheck, new ParameterExpression[] { xParam });
}
}
...
}
public class Account : Base, IUniqueable<Account>
{
...
public new Expression<Func<Account, bool>> Unique
{
get
{
var xParam = Expression.Parameter(typeof(Account), typeof(Account).Name);
MemberExpression leftExprNameCheck = MemberExpression.Property(xParam, "Name");
Expression rightExprNameCheck = Expression.Constant(this.Name);
BinaryExpression binaryExprNameCheck = MemberExpression.Equal(leftExprNameCheck, rightExprNameCheck);
MemberExpression leftExprFieldCheck = MemberExpression.Property(xParam, "AccountCode");
Expression rightExprFieldCheck = Expression.Constant(this.AccountCode);
BinaryExpression binaryExprFieldCheck = MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
BinaryExpression binaryExprAllCheck = Expression.OrElse(binaryExprNameCheck, binaryExprFieldCheck);
return Expression.Lambda<Func<Account, bool>>(binaryExprAllCheck, new ParameterExpression[] { xParam });
}
}
...
}
public static class Manager
{
...
public static T Save<T>(T item) where T : Base, new()
{
if (!item.IsValid)
{
throw new ValidationException("Unable to save item, item is not valid", item.GetRuleViolations());
}
if (item.Id == Guid.Empty && repository.GetAll<T>().Any(((IUniqueable<T>)item).Unique))
{
throw new Exception("Item is not unique");
}
return repository.Save<T>(item);
}
...
}
Essentially by implementing the IUniqueable interface for a specific type I can return a different Expression for each type. All good :-)

Get name of property as a string

(See below solution I created using the answer I accepted)
I'm trying to improve the maintainability of some code involving reflection. The app has a .NET Remoting interface exposing (among other things) a method called Execute for accessing parts of the app not included in its published remote interface.
Here is how the app designates properties (a static one in this example) which are meant to be accessible via Execute:
RemoteMgr.ExposeProperty("SomeSecret", typeof(SomeClass), "SomeProperty");
So a remote user could call:
string response = remoteObject.Execute("SomeSecret");
and the app would use reflection to find SomeClass.SomeProperty and return its value as a string.
Unfortunately, if someone renames SomeProperty and forgets to change the 3rd parm of ExposeProperty(), it breaks this mechanism.
I need to the equivalent of:
SomeClass.SomeProperty.GetTheNameOfThisPropertyAsAString()
to use as the 3rd parm in ExposeProperty so refactoring tools would take care of renames.
Is there a way to do this?
Okay, here's what I ended up creating (based upon the answer I selected and the question he referenced):
// <summary>
// Get the name of a static or instance property from a property access lambda.
// </summary>
// <typeparam name="T">Type of the property</typeparam>
// <param name="propertyLambda">lambda expression of the form: '() => Class.Property' or '() => object.Property'</param>
// <returns>The name of the property</returns>
public string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
var me = propertyLambda.Body as MemberExpression;
if (me == null)
{
throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
}
return me.Member.Name;
}
Usage:
// Static Property
string name = GetPropertyName(() => SomeClass.SomeProperty);
// Instance Property
string name = GetPropertyName(() => someObject.SomeProperty);
Now with this cool capability, it's time to simplify the ExposeProperty method. Polishing doorknobs is dangerous work...
With C# 6.0, this is now a non-issue as you can do:
nameof(SomeProperty)
This expression is resolved at compile-time to "SomeProperty".
MSDN documentation of nameof.
Using GetMemberInfo from here: Retrieving Property name from lambda expression you can do something like this:
RemoteMgr.ExposeProperty(() => SomeClass.SomeProperty)
public class SomeClass
{
public static string SomeProperty
{
get { return "Foo"; }
}
}
public class RemoteMgr
{
public static void ExposeProperty<T>(Expression<Func<T>> property)
{
var expression = GetMemberInfo(property);
string path = string.Concat(expression.Member.DeclaringType.FullName,
".", expression.Member.Name);
// Do ExposeProperty work here...
}
}
public class Program
{
public static void Main()
{
RemoteMgr.ExposeProperty("SomeSecret", () => SomeClass.SomeProperty);
}
}
Okay, here's what I ended up creating (based upon the answer I selected and the question he referenced):
// <summary>
// Get the name of a static or instance property from a property access lambda.
// </summary>
// <typeparam name="T">Type of the property</typeparam>
// <param name="propertyLambda">lambda expression of the form: '() => Class.Property' or '() => object.Property'</param>
// <returns>The name of the property</returns>
public string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
var me = propertyLambda.Body as MemberExpression;
if (me == null)
{
throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
}
return me.Member.Name;
}
Usage:
// Static Property
string name = GetPropertyName(() => SomeClass.SomeProperty);
// Instance Property
string name = GetPropertyName(() => someObject.SomeProperty);
There's a well-known hack to extract it from lambda expression (this is from the PropertyObserver class, by Josh Smith, in his MVVM foundation):
private static string GetPropertyName<TPropertySource>
(Expression<Func<TPropertySource, object>> expression)
{
var lambda = expression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
Debug.Assert(memberExpression != null,
"Please provide a lambda expression like 'n => n.PropertyName'");
if (memberExpression != null)
{
var propertyInfo = memberExpression.Member as PropertyInfo;
return propertyInfo.Name;
}
return null;
}
Sorry, this was missing some context. This was part of a larger class where TPropertySource is the class containing the property. You could make the function generic in TPropertySource to extract it from the class. I recommend taking a look at the full code from the MVVM Foundation.
The PropertyInfo class should help you achieve this, if I understand correctly.
Type.GetProperties() method
PropertyInfo[] propInfos = typeof(ReflectedType).GetProperties();
propInfos.ToList().ForEach(p =>
Console.WriteLine(string.Format("Property name: {0}", p.Name));
Is this what you need?
You can use Reflection to obtain the actual names of the properties.
http://www.csharp-examples.net/reflection-property-names/
If you need a way to assign a "String Name" to a property, why don't you write an attribute that you can reflect over to get the string name?
[StringName("MyStringName")]
private string MyProperty
{
get { ... }
}
I modified your solution to chain over multiple properties:
public static string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
MemberExpression me = propertyLambda.Body as MemberExpression;
if (me == null)
{
throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
}
string result = string.Empty;
do
{
result = me.Member.Name + "." + result;
me = me.Expression as MemberExpression;
} while (me != null);
result = result.Remove(result.Length - 1); // remove the trailing "."
return result;
}
Usage:
string name = GetPropertyName(() => someObject.SomeProperty.SomeOtherProperty);
// returns "SomeProperty.SomeOtherProperty"
Old question, but another answer to this question is to create a static function in a helper class that uses the CallerMemberNameAttribute.
public static string GetPropertyName([CallerMemberName] String propertyName = null) {
return propertyName;
}
And then use it like:
public string MyProperty {
get { Console.WriteLine("{0} was called", GetPropertyName()); return _myProperty; }
}
Based on the answer which is already in the question and on this article: https://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/ I am presenting my solution to this problem:
public static class PropertyNameHelper
{
/// <summary>
/// A static method to get the Propertyname String of a Property
/// It eliminates the need for "Magic Strings" and assures type safety when renaming properties.
/// See: http://stackoverflow.com/questions/2820660/get-name-of-property-as-a-string
/// </summary>
/// <example>
/// // Static Property
/// string name = PropertyNameHelper.GetPropertyName(() => SomeClass.SomeProperty);
/// // Instance Property
/// string name = PropertyNameHelper.GetPropertyName(() => someObject.SomeProperty);
/// </example>
/// <typeparam name="T"></typeparam>
/// <param name="propertyLambda"></param>
/// <returns></returns>
public static string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
var me = propertyLambda.Body as MemberExpression;
if (me == null)
{
throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
}
return me.Member.Name;
}
/// <summary>
/// Another way to get Instance Property names as strings.
/// With this method you don't need to create a instance first.
/// See the example.
/// See: https://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/
/// </summary>
/// <example>
/// string name = PropertyNameHelper((Firma f) => f.Firmenumsatz_Waehrung);
/// </example>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TReturn"></typeparam>
/// <param name="expression"></param>
/// <returns></returns>
public static string GetPropertyName<T, TReturn>(Expression<Func<T, TReturn>> expression)
{
MemberExpression body = (MemberExpression)expression.Body;
return body.Member.Name;
}
}
And a Test which also shows the usage for instance and static properties:
[TestClass]
public class PropertyNameHelperTest
{
private class TestClass
{
public static string StaticString { get; set; }
public string InstanceString { get; set; }
}
[TestMethod]
public void TestGetPropertyName()
{
Assert.AreEqual("StaticString", PropertyNameHelper.GetPropertyName(() => TestClass.StaticString));
Assert.AreEqual("InstanceString", PropertyNameHelper.GetPropertyName((TestClass t) => t.InstanceString));
}
}
You can use the StackTrace class to get the name of the current function, (or if you put the code in a function, then step down a level and get the calling function).
See http://msdn.microsoft.com/en-us/library/system.diagnostics.stacktrace(VS.71).aspx
I've been using this answer to great effect: Get the property, as a string, from an Expression<Func<TModel,TProperty>>
I realize I already answered this question a while back. The only advantage my other answer has is that it works for static properties. I find the syntax in this answer much more useful because you don't have to create a variable of the type you want to reflect.
I had some difficulty using the solutions already suggested for my specific use case, but figured it out eventually. I don't think my specific case is worthy of a new question, so I am posting my solution here for reference. (This is very closely related to the question and provides a solution for anyone else with a similar case to mine).
The code I ended up with looks like this:
public class HideableControl<T>: Control where T: class
{
private string _propertyName;
private PropertyInfo _propertyInfo;
public string PropertyName
{
get { return _propertyName; }
set
{
_propertyName = value;
_propertyInfo = typeof(T).GetProperty(value);
}
}
protected override bool GetIsVisible(IRenderContext context)
{
if (_propertyInfo == null)
return false;
var model = context.Get<T>();
if (model == null)
return false;
return (bool)_propertyInfo.GetValue(model, null);
}
protected void SetIsVisibleProperty(Expression<Func<T, bool>> propertyLambda)
{
var expression = propertyLambda.Body as MemberExpression;
if (expression == null)
throw new ArgumentException("You must pass a lambda of the form: 'vm => vm.Property'");
PropertyName = expression.Member.Name;
}
}
public interface ICompanyViewModel
{
string CompanyName { get; }
bool IsVisible { get; }
}
public class CompanyControl: HideableControl<ICompanyViewModel>
{
public CompanyControl()
{
SetIsVisibleProperty(vm => vm.IsVisible);
}
}
The important part for me is that in the CompanyControl class the compiler will only allow me to choose a boolean property of ICompanyViewModel which makes it easier for other developers to get it right.
The main difference between my solution and the accepted answer is that my class is generic and I only want to match properties from the generic type that are boolean.
it's how I implemented it , the reason behind is if the class that you want to get the name from it's member is not static then you need to create an instanse of that and then get the member's name. so generic here comes to help
public static string GetName<TClass>(Expression<Func<TClass, object>> exp)
{
MemberExpression body = exp.Body as MemberExpression;
if (body == null)
{
UnaryExpression ubody = (UnaryExpression)exp.Body;
body = ubody.Operand as MemberExpression;
}
return body.Member.Name;
}
the usage is like this
var label = ClassExtension.GetName<SomeClass>(x => x.Label); //x is refering to 'SomeClass'

Categories

Resources