У меня очень большая проблема, и я не могу найти кого-то еще в интернете, у которого есть моя проблема. Я очень надеюсь, что StackOverflow поможет мне ...
Я пишу приложение ASP.NET MVC и использую концепцию репозитория с Linq To Sql в качестве хранилища данных. Все отлично работает в отношении выбора строк из представлений. И отловить очень простые ограничения бизнес-правил. Тем не менее, я столкнулся с проблемой в моих отображениях хранимых процедур для удалений, вставок и обновлений. Позвольте мне объяснить:
Наш администратор БД провел большую работу по внедрению бизнес-логики во все наши хранимые процедуры, чтобы мне не пришлось беспокоиться об этом с моей стороны. Конечно, я делаю базовую проверку, но он управляет целостностью данных и конфликтующими ограничениями дат и т. Д. Проблема, с которой я столкнулся, состоит в том, что все хранимые процедуры (и я имею в виду все) имеют 5 дополнительных параметров (6 для вставок ), которые предоставляют мне информацию. Идея состоит в том, что когда что-то ломается, я могу предложить пользователю соответствующую информацию из нашей базы данных.
Например:
sp_AddCategory(
@userID INT,
@categoryName NVARCHAR(100),
@isActive BIT,
@errNumber INT OUTPUT,
@errMessage NVARCHAR(1000) OUTPUT,
@errDetailLogID INT OUTPUT,
@sqlErrNumber INT OUTPUT,
@sqlErrMessage NVARCHAR(1000) OUTPUT,
@newRowID INT OUTPUT)
Из описанной выше хранимой процедуры первые 3 параметра являются единственными параметрами, которые используются для «создания» записи категории. Остальные параметры просто используются, чтобы сообщить мне, что произошло внутри метода. Если бизнес-правило нарушено внутри хранимой процедуры, он НЕ использует ключевое слово SQL RAISEERROR, когда бизнес-правила нарушены. Вместо этого он предоставляет мне информацию об ошибке, используя параметры OUTPUT. Он делает это для каждой хранимой процедуры в нашей базе данных, даже для обновлений и удалений. Все вызовы «Get» выполняются с использованием пользовательских представлений. Все они были протестированы, и идея состояла в том, чтобы облегчить мою работу, поскольку мне не нужно добавлять бизнес-логику, чтобы перехватывать все различные сценарии для обеспечения качества данных.
Как я уже сказал, я использую Linq To Sql, и теперь я столкнулся с проблемой. Проблема в том, что у моего объекта модели "Категория" просто есть 4 свойства: CategoryID, CategoryName, UserId и IsActive. Когда я открыл конструктор и начал сопоставлять свои свойства для вставки, я понял, что для меня нет (простого) способа учесть дополнительные параметры, если я не добавлю их в свой объект Model.
Теоретически, мне хотелось бы сделать следующее:
// note: Repository Methods
public void AddCategory(Category category)
{
_dbContext.Categories.InsertOnSubmit(category);
}
public void Save()
{
_dbContext.SubmitChanges();
}
А затем из моего класса CategoryController я просто сделал бы следующее:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection collection)
{
var category = new Category();
try
{
UpdateModel(category); // simple validation here...
_repository.AddCategory(category);
_repository.Save(); // should get error here!!
return RedirectToAction("Index");
}
catch
{
// manage friendly messages here somehow... (??)
// ...
return View(category);
}
}
Каков наилучший способ справиться с этим, используя Linq to Sql? Я (лично) не чувствую, что имеет смысл добавлять все эти дополнительные свойства к каждому объекту модели ... Например, «Get» НИКОГДА не должен иметь ошибок, и я не хочу, чтобы мои методы репозитория возвращали одно тип объекта для вызовов Get, но принимайте объект другого типа для вызовов CUD.
Обновление: мое решение! (1 декабря 2009 г.)
Вот что я сделал, чтобы исправить мою проблему. Я избавился от моего метода Save () во всех моих репозиториях. Вместо этого я добавил метод «Update ()» в каждый репозиторий и фактически фиксирую данные в базе данных при каждом вызове CUD (т. Е. Create / Update / Delete).
Я знал, что каждая хранимая процедура имеет одинаковые параметры, поэтому я создал класс для их хранения:
public class MySprocArgs
{
private readonly string _methodName;
public int? Number;
public string Message;
public int? ErrorLogId;
public int? SqlErrorNumber;
public string SqlErrorMessage;
public int? NewRowId;
public MySprocArgs(string methodName)
{
if (string.IsNullOrEmpty(methodName))
throw new ArgumentNullException("methodName");
_methodName = methodName;
}
public string MethodName
{
get { return _methodName; }
}
}
Я также создал MySprocException, который принимает MySprocArgs в своем конструкторе:
public class MySprocException : ApplicationException
{
private readonly MySprocArgs _args;
public MySprocException(MySprocArgs args) : base(args.Message)
{
_args = args;
}
public int? ErrorNumber
{
get { return _args.Number; }
}
public string ErrorMessage
{
get { return _args.Message; }
}
public int? ErrorLogId
{
get { return _args.ErrorLogId; }
}
public int? SqlErrorNumber
{
get { return _args.SqlErrorNumber; }
}
public string SqlErrorMessage
{
get { return _args.SqlErrorMessage; }
}
}
Теперь вот где все собралось вместе ... Используя пример, который я начал в своем первоначальном запросе, вот как может выглядеть метод AddCategory ():
public void AddCategory(Category category)
{
var args = new MySprocArgs("AddCategory");
var result = _dbContext.AddWidgetSproc(
category.CreatedByUserId,
category.Name,
category.IsActive,
ref args.Number, // <-- Notice use of 'args'
ref args.Message,
ref args.ErrorLogId,
ref args.SqlErrorNumber,
ref args.SqlErrorMessage,
ref args.NewRowId);
if (result == -1)
throw new MySprocException(args);
}
Теперь из моего контроллера я просто делаю следующее:
[HandleError(ExceptionType = typeof(MySprocException), View = "SprocError")]
public class MyController : Controller
{
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Category category)
{
if (!ModelState.IsValid)
{
// manage friendly messages
return View(category);
}
_repository.AddCategory(category);
return RedirectToAction("Index");
}
}
Хитрость в управлении новым MySprocException
заключается в том, чтобы просто перехватить его с помощью атрибута HandleError и перенаправить пользователя на страницу, которая понимает MySprocException.
Надеюсь, это кому-нибудь поможет. :)