"SqlException: имя переменной" @_User_1_Id "уже объявлено" после обновления до Asp.Net Core 2.2
У меня есть приложение, которое работает в Asp.Net Core 2.1.6. что я пытаюсь обновить до 2.2. Я начал получать следующее исключение SQL при использовании 2.2:
SqlException: имя переменной '@__User_1_Id' уже объявлено. Имена переменных должны быть уникальными в пакете запроса или хранимой процедуре.
Я могу скомпилировать код, нацеленный на 2.2, но запустить его с использованием среды выполнения 2.1.6, и я не получаю сообщение об ошибке, поэтому я знаю, что что-то изменилось с 2.1.6, в результате чего появилась эта ошибка.
Исключение возникает при попытке получить все "запросы на передачу", которые были "перенаправлены" текущему пользователю. По сути, пользователю разрешено видеть любой запрос, который был ему перенаправлен. Мое приложение использует шаблон спецификации для построения запросов, поэтому вся логика фильтрации создается путем объединения Expression
объекты. Я смог сузить ошибку до HasBeenForwardedTo
спецификация, которая используется для фильтрации списка запросов вплоть до тех, которые были направлены указанному пользователю:
public class HasBeenForwardedTo : SpecificationBase<TransferRequest>
{
public ApplicationUser User { get; set; }
/// <summary>
/// Specification for determining if the request has been forwarded to the provided user.
/// </summary>
public HasBeenForwardedTo(ApplicationUser user)
{
this.User = user;
}
public override Expression<Func<TransferRequest, bool>> ToExpression()
{
// we only care about people that have been forwarded the request since the
// last "awaiting approval" event, so ignore any "forwarded to" events that
// happened before then.
var projection = CommonExpressions.LastAwaitingApprovalEvent();
var requestParam = Expression.Parameter(typeof(TransferRequest));
var requestToEvent = projection.Body.ReplaceParameter(projection.Parameters[0], requestParam);
// A request is considered "forwarded to" a user if the user has a "forwarded"
// event on the request since the last time it was "awaiting reviewing"
Expression<Func<TransferRequest, TransferRequestEvent, bool>> condition =
(rqst, evt) =>
// If there are no "Awaiting Review" events, check to see if the user
// has any "Forwarded" events at all.
(evt == null &&
rqst.TransferRequestEvents.Any(e2 =>
Equals(e2.EventType, EventType.Forwarded) && Equals(e2.User, User))) ||
// If there is an "Awaiting Review" event, only use events that have
// occurred *after* it.
(evt != null &&
rqst.TransferRequestEvents.Any(e2 =>
Equals(e2.EventType, EventType.Forwarded) &&
Equals(e2.User, User) &&
(e2.EventDateTime > evt.EventDateTime || (e2.EventDateTime == evt.EventDateTime && e2.Id > evt.Id))));
//combine the expression to for a new Exression<Func<TransferRequest,bool>>
//consolidating their parameters.
var body = condition.Body
.ReplaceParameter(condition.Parameters[0], requestParam)
.ReplaceParameter(condition.Parameters[1], requestToEvent);
var ret = Expression.Lambda<Func<TransferRequest, bool>>(body, requestParam);
return ret;
}
}
Этот код использует некоторые другие вспомогательные методы.
Метод Extension для объединения выражений таким образом, чтобы вывод одного выражения использовался в качестве ввода для другого выражения:
public static Expression<Func<TSource, TResult>> Compose<TSource, TIntermediate, TResult>(
this Expression<Func<TSource, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TSource));
var intermediateValue = first.Body.ReplaceParameter(first.Parameters[0], param);
var body = second.Body.ReplaceParameter(second.Parameters[0], intermediateValue);
return Expression.Lambda<Func<TSource, TResult>>(body, param);
}
Метод расширения для замены всех ссылок на параметр другим параметром.
public static Expression ReplaceParameter(this Expression expression,
ParameterExpression toReplace,
Expression newExpression)
{
return new ParameterReplaceVisitor(toReplace, newExpression)
.Visit(expression);
}
И ExpressionVisitor, который остро выполняет работу:
public class ParameterReplaceVisitor : ExpressionVisitor
{
private ParameterExpression from;
private Expression to;
public ParameterReplaceVisitor(ParameterExpression from, Expression to)
{
this.from = from;
this.to = to;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return node == from ? to : base.VisitParameter(node);
}
}