将存储过程与具有附加参数的 Linq To Sql 结合使用
-
07-07-2019 - |
题
我有一个非常大的问题,而且似乎在互联网上找不到其他人有我的问题。我当然希望 StackOverflow 能帮助我......
我正在编写一个 ASP.NET MVC 应用程序,并使用存储库概念和 Linq To Sql 作为我的数据存储。从视图中选择行方面一切都很顺利。并捕获非常基本的业务规则约束。但是,我在删除、插入和更新的存储过程映射中遇到了问题。让我解释:
我们的 DBA 投入了大量的工作将业务逻辑放入我们所有的存储过程中,这样我就不必担心它了。当然,我进行基本验证,但他管理数据完整性和冲突的日期约束等......我面临的问题是所有存储过程(我的意思是全部)都有 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 参数向我提供有关错误的信息。他对我们数据库中的每个存储过程(甚至更新和删除)都执行此操作。所有“获取”调用都是使用自定义视图完成的。它们都经过了测试,目的是让我的工作变得更轻松,因为我不必添加业务逻辑来捕获所有不同的场景以确保数据质量。
正如我所说,我正在使用 Linq To Sql,现在遇到了一个问题。问题是我的“类别”模型对象只有 4 个属性:CategoryID、CategoryName、UserId 和 IsActive。当我打开设计器开始映射插入的属性时,我意识到实际上没有(简单)方法可以解释附加参数,除非将它们添加到我的模型对象中。
理论上我想做的是:
// 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 调用。
更新:我的解决方案!(十二月2009 年 1 月)
这是我为解决问题所做的事情。我在所有存储库上删除了“Save()”方法。相反,我向每个存储库添加了一个“Update()”方法,并实际将数据提交到每个 CUD 上的数据库(即创建/更新/删除)调用。
我知道每个存储过程都有相同的参数,所以我创建了一个类来保存它们:
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 的页面。
我希望这对某人有帮助。:)
解决方案
我不相信您可以将输出参数添加到任何LINQ类中,因为参数不会在数据库的任何表中持久存在。
但您可以通过以下方式处理LINQ中的输出参数。
使用设计器添加您希望调用.dbml的存储过程。
在代码中调用存储过程
using (YourDataContext context = new YourDataContext())
{
Nullable<int> errNumber = null;
String errMessage = null;
Nullable<int> errDetailLogID = null;
Nullable<int> sqlErrNumber = null;
String sqlErrMessage = null;
Nullable<int> newRowID = null;
Nullable<int> userID = 23;
Nullable<bool> isActive=true;
context.YourAddStoredProcedure(userID, "New Category", isActive, ref errNumber, ref errMessage, ref errDetailLogID, ref sqlErrNumber, ref sqlErrMessage, ref newRowID);
}
其他提示
我还没有尝试过,但你可以看看这篇文章,在那里他谈到了返回输出参数的存储过程。
基本上将存储过程拖到LINQ to SQL设计器中然后它应该为您完成工作。
这 dbContext.SubmitChanges();
仅适用于实体框架。我建议保存、更新和删除将通过使用单个存储过程或使用 3 个不同的过程来工作。