Reputation: 2229
I've got a generic repository implementation that allows to pass a selector in order to declare the entities primary key property:
public abstract class RepositoryBase<TEntity, TKey>
where TEntity : class
{
private readonly Func<TEntity, TKey> _keySelector;
protected Func<TEntity, TKey> KeySelector {
get {
return _keySelector;
}
}
protected RepositoryBase(Func<TEntity, TKey> selector) {
_keySelector = selector;
}
}
... which can be used like this:
public class UserRepository : RepositoryBase<User, Guid>
{
public UserRepository()
: base((user) => user.Id)
{
}
}
I have now implemented a in memory repository to do some unit tests where I'd like to generate a new identity for each entity that is getting persisted. In case the entity does not have public accessor for the key, I've created a extension method to set properties using reflection.
public static void SetProperty<T, TProperty>(this T instance, Expression<Func<T, TProperty>> selector,
TProperty newValue)
where T : class
{
if (instance == null)
throw new ArgumentNullException("instance");
if (selector == null)
throw new ArgumentNullException("selector");
var propertyInfo = selector.GetMember() as PropertyInfo;
if (propertyInfo == null)
throw new InvalidOperationException();
propertyInfo.SetValue(instance, newValue);
}
My question is now: How can I use the KeySelector as an expression in order to set the primary key value? Is there a way to convert it? Or are there better ways to achieve what I'm trying?
Like so? Does that even make sense?:
protected override void AddItem(TEntity entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
var id = default(TKey);
if (GetPrimaryKey(entity).Equals(default(TKey)))
{
id = _identifierGenerator.Generate();
entity.SetProperty(x => GetPrimaryKey(x), id); // <----
}
_items[id] = entity;
}
Some methods used above:
public TKey GetPrimaryKey(TEntity entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
return KeySelector(entity);
}
public static MemberInfo GetMember<T, TProperty>(this Expression<Func<T, TProperty>> expression)
{
var memberExp = RemoveUnary(expression.Body);
return memberExp == null ? null : memberExp.Member;
}
private static MemberExpression RemoveUnary(Expression toUnwrap)
{
var unwrap = toUnwrap as UnaryExpression;
if (unwrap != null)
{
return unwrap.Operand as MemberExpression;
}
return toUnwrap as MemberExpression;
}
Upvotes: 0
Views: 1083
Reputation: 1503859
You can't, basically - not in any useful way. The solution seems simple though - change the type of your property instead:
private readonly Expression<Func<TEntity, TKey>> _keySelector;
protected Expression<Func<TEntity, TKey>> KeySelector {
get {
return _keySelector;
}
}
protected RepositoryBase(Expression<Func<TEntity, TKey>> selector) {
_keySelector = selector;
}
You can still use a lambda expression to initialize the property, and you can compile the expression tree to a delegate if you really need to.
Upvotes: 3