存储库模式和 LinqToSql 方法

本文关键字:LinqToSql 方法 模式 存储 | 更新日期: 2023-09-27 18:33:32

我有一个封装linq to sql数据上下文的对象。

它有一个方法IQuerable<T> FromStore<T>,允许我通过 linq to sql 从适当的表中获取我的实体。

我也有调用方法来调用数据上下文上的函数。

我现在遇到的问题是我不能在 where 子句中使用这些传递方法,因为没有到 sql 的转换。我理解为什么会发生这种情况,但我该如何解决它?


示例代码:

在我的存储库类中:

public class AquaReportsRepository : LinqToSqlRepository<int>, IAquaReportsRepository
{
    public bool IsPhysicalItem(string itemNumber)
    {
        return _UnderlyingDataContext.Aqua_IsPhysicalItem(itemNumber) ?? true;
    }
}

尝试从中访问数据:

from part in Repository.FromStore<Parts>()
where !(Repository.IsPhysicalItem(part.Item)) // eek, not translation to sql
select part.ItemNumber;

过去,我需要数据对象上的简单属性,从另一个属性计算,我使用 QueryMap 将该属性转换为等效表达式,但是我认为我不能在这里这样做,因为我需要访问 datacontext 方法。

存储库模式和 LinqToSql 方法

TL;DR:阅读第 1 点的示例。使用 QueryMap 中的代码,在下面以粗体进行更改。


是的,我已经设法通过对 QueryMap 进行一些轻微的修改来解决这个问题

我需要做两件事:

  1. 弄清楚如何告诉查询映射调用使用数据上下文的方法?

  2. 弄清楚如何让 querymap 在通过接口访问时获取类上定义的属性。

第一部分相当简单:

private static readonly ExpressionMethod<Func<AquaReportsRepository, string, bool>> _IsPhysicalItem =
    ExpressionMethod.Create((AquaReportsRepository ardc, string i) => ardc._AquaReportsDC.Aqua_IsPhysicalItem(i) ?? true);
[MapToExpression("_IsPhysicalItem")]
public bool IsPhysicalItem(string itemNumber)
{
    return _IsPhysicalItem.Invoke(this, itemNumber);
}

这里的关键是简单地使用一个函数,除了正常的其他参数之外,该函数将对象本身作为第一个参数(在这种情况下AquaReportsRepository(。

然而,第二部分需要对MappedQueryVisitor.cs进行一些(相当小的(更改。 在这两种情况下,只有一个 if 语句(里面有语句!(。

将现有的GetLambda方法替换为:

private LambdaExpression GetLambda(Expression receiver, MemberInfo member) {
    LambdaExpression exp;
    if(!_mappings.TryGetValue(member, out exp)) {
        exp = _mappingLookup(member);
        if(null == exp) {
            var attr = (MapToExpressionAttribute) member.GetCustomAttributes(typeof(MapToExpressionAttribute), true).FirstOrDefault();
            // Added by me to deal with interfaces
            if (null == attr)
            {
                if (null != receiver)
                {
                    // member could be an interface's member, so check the receiver.object type
                    if (receiver.NodeType == ExpressionType.Constant)
                    {
                        var receiverType = ((ConstantExpression)receiver).Value.GetType();
                        var receiverMemberInfo = receiverType.GetMembers().Where(mi => mi.Name == member.Name).SingleOrDefault();
                        if (null != receiverMemberInfo)
                        {
                            attr = (MapToExpressionAttribute)receiverMemberInfo.GetCustomAttributes(typeof(MapToExpressionAttribute), true).FirstOrDefault();
                            member = receiverMemberInfo;
                        }
                    }
                }
            }

            if(null != attr) {
                exp = GetLambdaFromAttribute(receiver, member.DeclaringType, attr);
            }
        }
        _mappings.Add(member, exp);
    }
    return exp;
}

此更改意味着,如果我们有一个表示接口方法的member MethodInfo对象,我们将识别它并尝试获取我们正在处理的具体类型(由常量表达式定义(的实际MethodInfo

现在,在给定接口MemberInfo的情况下,它正确地选取实现类上的 MapToExpressionAttribute 属性。

下一个问题是 VisitMethodCall ,其中从属性调用替换表达式失败,因为参数表达式是接口类型,通过我们调用的方法需要实现类。

最后的代码更改纠正了这一点。

CollectArguments方法更改为:

private static IEnumerable<Expression> CollectArguments(MethodCallExpression m) {
    IEnumerable<Expression> args = m.Arguments;
    if (!m.Method.IsStatic)
    {
        var objectExpression = m.Object;
        // Added by me, to deal with interfaces
        if (objectExpression.NodeType == ExpressionType.Constant)
        {
            var objectConstExpression = ((ConstantExpression)objectExpression);
            if (objectConstExpression.Type.IsInterface)
            {
                objectExpression = Expression.Constant(objectConstExpression.Value);
            }
        }
        args = Enumerable.Repeat(objectExpression, 1).Concat(args);
    }
    return args;
}