【问题标题】:Convert Func<T, TProperty> to Expression<Func<T, Property>>将 Func<T, TProperty> 转换为 Expression<Func<T, Property>>
【发布时间】:2014-09-12 14:29:27
【问题描述】:

我有一个通用存储库实现,它允许传递选择器以声明实体主键属性:

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;
    }

}

... 可以这样使用:

public class UserRepository : RepositoryBase<User, Guid>
{
    public UserRepository()
        : base((user) => user.Id)
    {

    }
}

我现在已经实现了一个 in memory 存储库 来做一些单元测试,我想为每个被持久化的实体生成一个新的身份。如果实体没有密钥的公共访问器,我创建了一个扩展方法来使用反射设置属性。

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);
    }

我现在的问题是:如何使用 KeySelector 作为 表达式 来设置主键值?有没有办法转换它?还是有更好的方法来实现我正在尝试的目标?

像这样?这有意义吗?:

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;
    }

上面用到的一些方法:

方法'GetPrimaryKey'

public TKey GetPrimaryKey(TEntity entity)
{
    if (entity == null)
        throw new ArgumentNullException("entity");

    return KeySelector(entity);
}

方法'GetMember'

public static MemberInfo GetMember<T, TProperty>(this Expression<Func<T, TProperty>> expression)
    {
        var memberExp = RemoveUnary(expression.Body);

        return memberExp == null ? null : memberExp.Member;
    }

方法'RemoveUnary'

    private static MemberExpression RemoveUnary(Expression toUnwrap)
    {
        var unwrap = toUnwrap as UnaryExpression;
        if (unwrap != null)
        {
            return unwrap.Operand as MemberExpression;
        }

        return toUnwrap as MemberExpression;
    }

【问题讨论】:

  • 你可以去Expression&lt;Func&lt;T, TProperty&gt;&gt;Func&lt;T, TProperty&gt;,但你不能去其他方式。你能把GetPrimaryKey的代码显示一下吗,也许可以修改为返回一个表达式。
  • 您可以通过x =&gt; func(x) 轻松获得Expression&lt;Func&lt;T, TProperty&gt;&gt;,但它不适用于EnumerableQuery 以外的任何提供者,因此对于您可能想要的任何东西都几乎没有用处做。
  • @ScottChamberlain 没问题,我已经在上面添加了。
  • @hvd 是否有更好的方法来解决我正在尝试的问题?

标签: c# lambda expression repository-pattern


【解决方案1】:

你不能,基本上 - 没有任何有用的方式。解决方案似乎很简单 - 改为更改属性的类型:

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;
}

您仍然可以使用 lambda 表达式来初始化属性,如果您确实需要,您可以将表达式树编译为委托。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-20
    • 1970-01-01
    相关资源
    最近更新 更多