Вы не можете написать метод и ожидать, что L2S автоматически узнает, как взять ваш метод и перевести его на SQL.L2S знает о некоторых наиболее распространенных методах, предоставляемых как часть .NET Framework для примитивных типов.Все что угодно, кроме этого, и он не будет знать, как выполнить перевод.
Если вам нужно сохранить свою модель БД как есть:
Вы можете определить методы взаимодействия спользовательский формат и использовать их в запросах.Тем не менее, вам придется помочь L2S с переводом.Для этого вы должны искать вызовы ваших методов в дереве выражений, сгенерированных для вашего запроса, и заменять их реализацией, которую L2S может перевести.Один из способов сделать это - предоставить прокси-реализацию IQueryProvider
, которая проверяет дерево выражений для данного запроса и выполняет замену перед передачей его в L2S IQueryProvider
для перевода и выполнения.Дерево выражений, которое увидит L2S, можно преобразовать в SQL, поскольку оно содержит только простые арифметические операции, используемые в определениях ваших методов.
Если у вас есть возможность изменить модель БД:
Возможно, вам лучше использовать стандартный тип столбца DateTime
для ваших данных.Тогда вы можете смоделировать столбец как System.DateTime
и использовать его методы (что понимает L2S).Этого можно достичь, изменив саму таблицу или предоставив представление, которое выполняет преобразование, и обеспечив взаимодействие L2S с представлением.
Обновление : поскольку вам необходимо сохранить текущую модель, вы 'Я хочу перевести ваши методы для L2S.Наша цель - заменить вызовы некоторых конкретных методов в запросе L2S лямбда-выражением, которое L2S может перевести.Все остальные вызовы этих методов, конечно, будут выполняться нормально.Вот пример того, как вы могли бы это сделать ...
static class DateUtils
{
public static readonly Expression<Func<int, int>> GetMonthExpression = t => (t / 60 / 60 / 24) % 13;
static readonly Func<int, int> GetMonthFunction;
static DateUtils()
{
GetMonthFunction = GetMonthExpression.Compile();
}
public static int GetMonth(int t)
{
return GetMonthFunction(t);
}
}
Здесь у нас есть класс, который определяет лямбда-выражение для получения месяца из целочисленного времени.Чтобы не определять математику дважды, вы можете скомпилировать выражение и затем вызвать его из вашего GetMonth
метода, как показано здесь.В качестве альтернативы вы можете взять тело лямбды и скопировать его в тело метода GetMonth
.Это пропустит компиляцию выражения во время выполнения и, вероятно, будет выполняться быстрее - в зависимости от того, что вы предпочитаете.
Обратите внимание, что сигнатура GetMonthExpression
лямбда-выражения точно соответствует методу GetMonth
.Далее мы проверим выражение запроса, используя System.Linq.Expressions.ExpressionVisitor
, найдем вызовы к GetMonth
и заменим их на нашу лямбду, заменив t
значением первого аргумента на GetMonth
.
class DateUtilMethodCallExpander : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression node)
{
LambdaExpression Substitution = null;
//check if the method call is one we should replace
if(node.Method.DeclaringType == typeof(DateUtils))
{
switch(node.Method.Name)
{
case "GetMonth": Substitution = DateUtils.GetMonthExpression;
}
}
if(Substitution != null)
{
//we'd like to replace the method call; we'll need to wire up the method call arguments to the parameters of the lambda
var Replacement = new LambdaParameterSubstitution(Substitution.Parameters, node.Arguments).Visit(Substitution.Body);
return Replacement;
}
return base.VisitMethodCall(node);
}
}
class LambdaParameterSubstitution : ExpressionVisitor
{
ParameterExpression[] Parameters;
Expression[] Replacements;
public LambdaParameterExpressionVisitor(ParameterExpression[] parameters, Expression[] replacements)
{
Parameters = parameters;
Replacements = replacements;
}
protected override Expression VisitParameter(ParameterExpression node)
{
//see if the parameter is one we should replace
int p = Array.IndexOf(Parameters, node);
if(p >= 0)
{
return Replacements[p];
}
return base.VisitParameter(node);
}
}
Первый класс здесь посетит дерево выражений запросов и найдет ссылки на GetMonth
(или любой другой метод, требующий замены) и заменит вызов метода.Замена обеспечивается частично вторым классом, который проверяет заданное лямбда-выражение и заменяет ссылки на его параметры.
Преобразовав выражение запроса, L2S никогда не будет видеть вызовы ваших методов и теперь может выполнятьзапрос, как и ожидалось.
Чтобы перехватить запрос, прежде чем он попадет в L2S удобным способом, вы можете создать своего IQueryable
провайдера , который будет использоваться в качестве прокси передL2S.Вы должны выполнить вышеуказанные замены в своей реализации Execute
, а затем передать новое выражение запроса поставщику L2S.