Choosing an ORM strategy
One of the mistakes I see people make (and have made myself) is assuming that you must choose exactly one ORM strategy for an entire database/application/system. It’s simply not the case. You can (and should) tailor your ORM strategy to your use case, and make sure you’re picking the right tool (or feature of the tool) for the job.
First things first – 99.9% of the time, you should never use ADO.NET classes directly, including IDbCommand and IDataReader. If you’re still doing “dataReader.MoveNext” – you are writing code that you should not write, ever again. There are better options for what you’re trying to do.
Now, there are lots of folks who don’t like ORMs, and from listening on how those folks chose their ORM strategies, I would agree. Fowler said on OrmHate that:
much of the frustration with ORMs is about inflated expectations
We have to move past the idea that ORMs are bad or ugly or bloatware. ORMs are meant to solve a problem, with many different options for doing so. But before we look at options, let’s look at the problem space. What is the problem we’re trying to solve?
Bridging the gap
If you have to push/pull data to/from SQL and .NET, you have to map your .NET data types to SQL. In .NET, this means using ADO.NET to communicate SQL commands to a SQL database. At some point, mapping has to happen between SQL data types and .NET data types. SQL dates are different than .NET dates, and so on.
ADO.NET helps with this, but leaves the work up to you for taking raw result sets and populating objects. And ultimately, that’s what we want to do – work with objects and types in .NET, and let something flow our objects/data back and forth to SQL.
ORMs are meant to help solve this problem, by applying layers on top of ADO.NET of various abstractions and weight. But there are many different strategies for doing so, used to solve this problem in many different ways.
Let’s look at each of these in turn and see where they might fit.
Entity-based relational mapping
In entity-based relational mapping, tables are more or less 1:1 with entities in your object design. When you add a property to an object, a column is added to the database. Usage of entity-based relational mapping centers around loading up an entity (or aggregate) by its identifier, manipulating that object and perhaps child objects, and saving that object back through the ORM.
The ORM in this case provides many features, like:
- Change tracking
- Lazy-loading
- Eager fetching
- Cascades
- Identity map
- Unit of work tracking
If I’m working with effectively one entity or aggregate at a time, tools like NHibernate use mapping configuration to keep track of entities loaded and automatically flush changes upon committing the transaction. This is nice because we don’t have to transport a data access gateway around, it’s just managed for us.
When loading up an item by Id for manipulation purposes, this option works great. It removes quite a bit of code I would otherwise need to write to keep track of adding objects, saving them and so on. I would have to “remember” to save things and the like.
The downside is that the ORM does not immediately know that you’re just reading objects, or loading up an entity to manipulate it. We often see folks stumble when not understanding that change tracking is turned on by default, and when you load up entities, the assumption is that we need to check to see if it’s changed.
If you’re looking at loading up an entity in order to manipulate it and persist changes (or create a new entity to manipulate), this pattern allows a greater deal of freedom of keeping data access concerns into infrastructure layers, and let your entity types be relatively persistent-ignorant. Persistence ignorance is not that my C# model and DB model can diverge, but more that data access concerns don’t leak into my object model, which I’d rather be more concerned with business rules.
Active Record is another flavor of entity-based usage, except data access concerns are embedded with my object model.
Result-set-based relational mapping
In most applications, the demands for reads greatly outstrips the instances of writes. We’ve seen ratios of 100:1 looking at the profiler of SELECTs to INSERT/UPDATE/DELETEs in one recent application. When looking at what SQL is very good at, it’s great at working with data in sets. To return sets back to the application, it often makes zero sense to try and use an entity-based approach for simply returning sets of data.
But we still would like to not work with very raw SQL objects like IDataReader or DataTables. These are very loosely-typed objects, not easily transferable to upper layers of the application.
Instead, we often build objects who are tailor-made for data. These are often called Data-Transfer Objects, or Read Models. These are types we craft for individual SQL SELECT queries, not to be re-used across other queries.
Many ORMs have features optimized for these scenarios. In NHibernate, you can use projections to skip any sort of tracked Entity objects to map straight to DTOs, you can use SQL queries to map straight to DTOs and skip needing to configure mapping. Or you can use micro-ORMs like PetaPoco.
These reads can also stream objects as they are read. Both NHibernate and several micro-ORMs let you map individual DTO instances as each row is read from the underlying result set, minimizing the amount of objects kept around in memory.
In our applications, we often still use NHibernate for reads, but skip any kind of entity objects and instead craft our raw SQL, relying on NHibernate’s optimized mappers to simply supply a DTO type and the results are mapped automatically.
This approach does not work well if we have to apply business rules and persist information back, as these models usually map to result sets, not database tables.
DML-based relational mapping
If you know what you want to do in SQL for CRUD operations, and would rather not have a tool figure it out for you, you’re really looking for something to effectively abstract DML commands at a step higher than ADO.NET.
This is the arena of micro ORMs. Tools like PetaPoco, Dapper, Massive and others work to solve the problem of the pain of working with raw ADO.NET. These typically still allow us to work with objects, but our interactions are greatly simplified and SQL is brought to the forefront. We only need a connection, and these tools can let us work with all the CRUD operations in a manner that offers much simpler code that ADO.NET
In cases where you don’t already have an entity-based relational mapper in the application, micro-ORMs provide a much lighter approach. Because types and mappings are not configured ahead of time, these tools rely on lazily-evaluated optimized reflection caching techniques to on-the-fly map parameters and result sets.
Many applications can start out with DML-based mappings, graduating to a full-fledged ORM when certain relationships or entities demand it.
Bulk loading tools
One that holds a dear place in my heart – sometimes you don’t want to push/pull data in an object-based manner. Instead, you’d rather push and pull data in bulk. Tools like SQL Bulk Copy allow you to pull data out in a delimited or tabular format, or push data in.
Bulk loading tools typically work as a sort of bazooka, blasting data out or blasting data in, but not providing much beyond that. You won’t be able to do updates or deletes, but for getting large amounts of data in and out of SQL, bulk loading tools are what you need.
In many integration scenarios where you provide delimited files to partners, or delimited files are supplied to you, bulk loaders allow you to treat files as tables and load them directly against the database, skipping past layers of abstractions both in mapping objects and even in the database.
These tools are tuned to be much, much quicker than traditional methods of extracting/loading data. In some of our tests, we’ve seen orders of magnitude differences of time between row-by-row loading versus bulk load. In one case, we saw a difference between several hours and less than a minute to load.
The downside is that options are quite limited, and you’re really only limited to INSERT and SELECT. Everything else requires different tools.
Right tool for the job
In one recent project, I’ve used every single one of these approaches against a single database and a single codebase. NHibernate for entity/aggregate-based mapping, result-set for reading sets of data (and building messages/exports/views from the results), DML-based mappings for simple tables and modules, and bulk loading tools for those nightly drops of million-row files from partners.
The key is to not tie yourself down to a specific tool or approach. No single ORM strategy works for every situation, and nor should it. NHibernate can work for a variety of scenarios beyond entity-based mappings, but doesn’t do everything. Complexity often arises from trying to use the same approach for every situation.
Every application written outside of SQL uses an ORM. Whether it’s hand-rolled ADO.NET code, or NHibernate, you have to bridge the gap from .NET to SQL. Bridging this gap is a hard problem to solve, and nothing solves the entire problem set perfectly. Nor should it!
Choose the approach that fits the problem at hand, and don’t worry about having multiple approaches in a single solution. That’s not to say haphazard decisions are appropriate, but informed, educated decisions based on knowledge of all choices at hand are always a good idea.
from: http://lostechies.com/jimmybogard/2012/07/20/choosing-an-orm-strategy/
Choosing an ORM strategy的更多相关文章
- Inheritance with EF Code First: Part 3 – Table per Concrete Type (TPC)
Inheritance with EF Code First: Part 3 – Table per Concrete Type (TPC) This is the third (and last) ...
- selenium-网站demo学习-test Design-优化自动化代码
看selenium的网站的文档,里面的自动化用例设计有一些小点很靠谱.学了很多,可以用作优化自己的代码. 1.测试类型: Testing Static Content Testing Links Fu ...
- Character Encoding in .NET
https://docs.microsoft.com/en-us/dotnet/standard/base-types/character-encoding#Encodings Characters ...
- 6. Design Patterns with First-Class Functions
1. Refactoring Strategy 1.1 Classic Strategy from abc import ABC, abstractmethod from collections im ...
- selenium docs
Note to the Reader - Docs Being Revised for Selenium 2.0! Introduction Test Automation for Web Appli ...
- .NET ORM框架(一)
最近做项目自己整理了一个ORM框架,分享给大家看看,有很多不足望大家指出. 下面是使用方法 BLL 主要方法 逻辑层:子类继承父类, 直接用BASE调用 ManagerBLL 中的方法. public ...
- Hibernate第一篇【介绍Hibernate,简述ORM,快速入门】
前言 前面已经学过了Struts2框架了,紧接着就是学习Hibernate框架了-本博文主要讲解介绍Hibernate框架,ORM的概念和Hibernate入门 什么是Hibernate框架? Hib ...
- springboot~JPA把ORM统一起来
JPA介绍 JPA(Java Persistence API)是Sun官方提出的Java持久化规范.它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据.他的出现主要是为了简 ...
- 【ORM框架】Spring Data JPA(一)-- 入门
本文参考:spring Data JPA入门 [原创]纯干货,Spring-data-jpa详解,全方位介绍 Spring Data JPA系列教程--入门 一.Spring Data JPA介 ...
随机推荐
- Linux自身安全SElinux
查看SELinux状态: 1./usr/sbin/sestatus -v ##如果SELinux status参数为enabled即为开启状态 SELinux status: ...
- Django之进阶相关操作
一.QuerySet的特点 1.可切片 使用Python 的切片语法来限制查询集记录的数目 .它等同于SQL 的LIMIT 和OFFSET 子句. 1 >>> Entry.objec ...
- 《数据结构与STL-第二章 线性表》读书笔记
线性表 定义 线性表(linear list)是由零个或多个相同类型的数据元素构成的有限序列. 存储结构 顺序存储 最简单的存储方法是顺序存储法,即把线性表的数据元素按照逻辑次序顺序地放在一组地址连续 ...
- 【LOJ】 #2009. 「SCOI2015」小凸玩密室
题解 神仙dp啊QAQ 我们发现我们需要枚举一个起点,遍历完它所有的儿子然后向上爬 设\(f[i][j]\)表示第i个点的子树全部处理完之后到达i深度为j的祖先的兄弟处 我们只需要对叶子节点和只有一个 ...
- ul>li中自定义属性后取值的问题
动态赋值的li: $.ajax({ type: "POST", url: "${base}/before/subDemand/listType", succes ...
- XML DOM(Document Object Model)
1.XML DOM 是用于获取.更改.添加或删除 XML 元素的标准.2.节点(XML 文档中的每个成分都是一个节点): 整个文档是一个文档节点: 每个XML元素是一个元素 ...
- elementUI 学习入门之 radio 单选框
Radio 单选框 基础用法 选项默认可见,选项不宜过多,选项过多建议使用 select 选择器 使用 Radio 组件,需要设置 v-model 绑定变量,选中意味着变量的值为相应 Radio l ...
- 长沙理工大学第十二届ACM大赛-重现赛 L - 选择困难症
题目描述 小L有严重的选择困难症. 早上起床后,需要花很长时间决定今天穿什么出门. 假设一共有k类物品需要搭配选择,每类物品的个数为Ai,每个物品有一个喜欢值Vj,代表小L对这件物品的喜欢程度. 小L ...
- Xamarin.Forms教程开发Xamarin.Forms应用程序需要的工具
开发Xamarin.Forms应用程序需要的工具 Xamarin.Forms教程开发Xamarin.Forms应用程序需要的工具,2014年5月8日在发布的Xamrin 3中引进了Xamarin.Fo ...
- python tesseract 识别图片中的文字的乱码问题(ubuntu系统下)
OCR(Optical Character Recognition):光学字符识别,是指对图片文件中的文字进行分析识别,获取的过程. 首先,需要安装 tesseract-ocr(tesseract O ...