使用存储过程时最好的ORM是哪个?

7

我有一些业务对象(由开发人员编写)和一些存储过程(由数据库管理员编写)。

有没有人能推荐一个好的对象映射器来处理这种设置。

我尝试了Codesmith和NHibernate,但遇到了问题。 我不介意我的ORM是免费还是付费的。


2
我不明白为什么存储过程会被人们如此诟病。有时候它们确实是最好的选择。它们怎么会声名狼藉呢? - Cheeso
有很多人不喜欢它们。当涉及到调整查询性能时,它们非常好用,但对于维护来说却是一场噩梦,就像我的一个项目有400多个存储过程一样...感谢VSTS中的模式比较功能。 - Jojo
我正在寻找类似的解决方案,因为我们的新项目技术负责人坚持所有代码与数据库交互都必须通过存储过程进行。每个CRUD操作。每个查询。一切。但是,他希望拥有完全通用的DAL :-) - CMPalmer
@Cheeso 这不是存储过程的错,它们只是按照指示执行。存储过程在人力资源方面没有保护。想象一下,在 Gordian Knot 3.1.5 中调用 sp_GetUserById 进行调试。视图模型、控制器和服务都在做它们应该做的事情并通过了测试(你有测试,对吧?)。你检查数据,看起来没问题。"这个......" 你喃喃自语,再次加载 SSMS。它在那里!不仅存储过程中有业务逻辑,而且与上个月发布的新功能冲突。祝你好运,逃避责任吧! - CaptainMarvel
9个回答

7
SubSonic对存储过程提供了出色的支持。它将为每个存储过程包装一个帮助方法,如果需要,您可以从结果中检索强类型集合或实体。在这篇博客文章中,我展示了一种方法来实现这一点。只要您的存储过程返回与SELECT * FROM TableName相同的模式,它就可以与SubSonic实体一起使用。
至于基于数据库生成类,SubSonic会生成部分类,因此您可以根据需要进行扩展。您还可以从SubSonic生成的类映射到您的实际模型。

就像 Linq2Sql 一样,如何处理一个存储过程中的多个结果集,并将其映射到实体类呢? :) - eglasius
我相信您只能将多个结果集加载到数据集中。 - John Sheehan
1
Subsonic在实体化方面存在严重的性能问题。 - Sam Saffron
有没有更好的答案来回答这个问题?如果没有提供替代方案,那么在与问题无关的情况下抨击Subsonic有点奇怪。 - John Sheehan
好的,我回复了https://dev59.com/fEfRa4cB1Zd3GeqP5gR7#6645870 ... SubSonic不会提供多个记录集支持、输入/输出参数支持等等。 - Sam Saffron

6
免责声明:本人是Dapper的作者。
如果您正在寻找一个简单的对象映射程序,用于将过程映射到业务对象,则Dapper是一个很好的选择。请注意,它不包含“图形管理”、“身份映射”等功能。它提供了一个基础、完整的解决方案,涵盖了许多其他ORM不支持的场景。尽管如此,它提供了最快的对象实例化器之一,在某些基准测试中可以比EF甚至比subsonic快10倍甚至100倍。

琐碎的事情:

create proc spGetOrder
   @Id int
as 
select * from Orders where Id = @Id
select * from OrderItems where OrderId = @Id 

可以与以下内容进行映射:
var grid = cnn.QueryMultiple("spGetOrder", new {Id = 1}, commandType: CommandType.StoredProcedure);
var order = grid.Read<Order>();
order.Items = grid.Read<OrderItems>(); 

此外,您还支持以下内容:
  1. 多映射器,允许您将单行映射到多个对象
  2. 输入/输出/返回参数支持
  3. 用于特定于数据库的参数处理(如 TVPs)的可扩展接口
例如:
create proc spGetOrderFancy
   @Id int,
   @Message nvarchar(100) output 
as 
set @Message = N'My message' 
select * from Orders join Users u on OwnerId = u.Id where Id = @Id
select * from OrderItems where OrderId = @Id
return @@rowcount

可以映射为:
var p = new DynamicParameters(); 
p.Add("Id", 1);
p.Add("Message",direction: ParameterDirection.Output);
p.Add("rval",direction: ParameterDirection.ReturnValue);
var grid = cnn.QueryMultiple("spGetOrder", p, commandType: CommandType.StoredProcedure);
var order = grid.Read<Order,User,Order>((o,u) => {o.Owner = u; return o;});
order.Items = grid.Read<OrderItems>(); 

var returnVal = p.Get<int>("rval"); 
var message = p.Get<string>("message"); 

最后,Dapper还允许自定义参数实现:
public interface IDynamicParameters
{
   void AddParameters(IDbCommand command);
}

当实现这个接口时,您可以告诉Dapper您希望添加到命令中的参数。这样可以支持表值参数和其他特定于数据库的功能。


5

Subsonic有一个灵活的解决方案:

    class CustomerOrder {
        private string productName;

        public string ProductName {
            get { return productName; }
            set { productName = value; }
        }
        private int total;

        public int Total {
            get { return total; }
            set { total = value; }
        }

    }

然后:

List<CustomerOrder> orders = Northwind.SPs.CustOrderHist("ALFKI")
        .ExecuteTypedList<CustomerOrder>();

Subsonic是一种可靠的“瑞士军刀”式ORM。


2

不,除非我漏掉了什么,否则Nhibernate与sprocs不兼容。它要求返回的项目按特定顺序排列,并具有其他限制。 - Jojo

1

LINQ to SQL 设计器将在 DataContext 对象上作为方法提供类型安全的存储过程。您可以将其映射到对象以进行 CRUD 操作。

事实上,我正在做这件事的中间阶段。


我对LINQ to SQL的问题在于,微软已经基本上淘汰了这个产品,并将所有东西都放在了Entity Framework后面。 - Jojo
@Joe,不是很确定,我没有链接,但有另一个回答,我们得到了更新消息,说有一支团队正在使用linq2sql进行持续活动。 - eglasius
如果你有链接的话就太好了,但我会去谷歌一下看看能否找到它。据我所知,它最后被转移到了ADO团队,这是个死胡同。 - Jojo
@Joe,请查看Marc在此答案中的回复:http://stackoverflow.com/questions/653019/linq-to-sql-pitfalls/653201#653201 - eglasius

1

既然你有一个DBA编写存储过程,我认为最好的做法是与他密切合作,找出如何将表映射到对象,以及如何构建数据库使其与你的领域模型相匹配。存储过程并没有什么问题,只需要开发人员和DBA之间进行密切协作。

理想情况下,这位DBA应该是你项目团队的一部分...


0

我喜欢 Entity Framework 目前处理存储过程的方式。你可以将存储过程与实体的增删改查操作关联起来,它甚至会检测哪些存储过程与你的实体属性匹配。目前唯一的缺点是,如果你将一个存储过程与一个实体关联起来,你必须将所有的增删改查操作都与一个存储过程关联起来。

这篇 EF Sproc article 有一些很好的示例,展示了如何在 EF 中使用存储过程,并提供了一些非常好的扩展方法。


0
我认为这个主要的问题是,选择存储过程会失去ORM提供的很多灵活性,特别是在检索信息方面。因此,我确认你不能使用大多数ORM的所有功能。
例如,如果使用linq2sql,则几乎可以将其包装到存储过程中。您还可以将生成实体的插入、删除和更新映射到存储过程。您会失去很多内容,其中包括查询现在已固定(可能会检索到不必要的额外列或创建大量存储过程),以及懒惰加载。

更新: 我更偏向于使用 Linq to SQL,但我会再次审视你对 NHibernate 的假设。特别是,我怀疑它会强制列顺序,因为它配置了列名(参见http://nhibernate.info/blog/2008/11/23/populating-entities-from-stored-procedures-with-nhibernate.html)。它还支持一些我不知道如何在 Linq to SQL 中实现的功能:http://nhibernate.info/blog/2008/11/23/populating-entities-with-associations-from-stored-procedures-with-nhibernate.html。请注意,我并不是指 Linq to SQL 不支持最后一个功能,只是我不知道如何实现 ;)


0

现在你可以尝试使用Nfinity.Data,它具有对存储过程的强类型支持,包括表值参数(TVP)和输出参数。要读取任何结果,您需要手动访问阅读器(它提供了一个阅读器抽象层)。

我不太相信NHibernate或EF将实体映射到存储过程的想法,因为它们的内部工作方式是不确定的,即无法在运行时以任何方式进行验证。通过像上面那样的阅读器,在代码中完成所有操作更具声明性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接