ORMs and the DBA How to Make Both Happy
Thank You to All Our Sponsors!
What’s On Deck Introduction (Common) Problems with ORMs Mitigation Strategies Stored Procedure Solutions Dapper Questions
What Is An ORM? Object-relational mapping (ORM, O/RM, and O/R mapping tool) in computer science is a programming technique for converting data between incompatible type systems using object-oriented programming languages. This creates, in effect, a "virtual object database" that can be used from within the programming language. - https://en.wikipedia.org/wiki/Object-relational_mapping ORMs, at a minimum should provide a method of mapping Query result sets into object graphs
There are no silver bullets (or magic solutions) Some Caveats There are no silver bullets (or magic solutions) ORMs are a valuable tool for certain problems I’m not going to bash any one group more than another – everyone wants the same thing: success of the project! We are all right (and we are all wrong)
Address the imbalance between Relational and Object models Why Do We Use ORMs? Address the imbalance between Relational and Object models Usually provide automated mapping for queries Some provide the ability to generate SQL on the fly “No need” to write SQL Some provide the ability to specify the shape of the database in code “Code first” Viewed by developers as speeding development (although this is an arguable point)
“ORMs: They Suck” – Literally Everyone
We’ve all seen things like this…
Bad SQL (aka Auto-generating Code That Always Works Is Hard…) ORMs that generate their own SQL have generation strategies These strategies exist to ensure that the SQL will execute, no matter what sort of convoluted code is passed in (especially in the case of a LINQ-to-{x} scenario) The goal of generated SQL is to execute the query successfully, performance is a secondary (at best) concern SQL generation will usually not take advantage of the latest features (or even features available several versions ago) Less version-specific branching in the code leads to more reliable code ORMs usually depend on the consumer to handle Unit of Work scenarios (i.e. Transactions) ORMs usually depend on the consumer to make sure all the data is loaded when it’s needed (i.e. the N+1 query problem, the too-lazy loading problem)
A Comparison of Common Complaints About ORMs Code First migrations never work The DBAs hate them They’re difficult to unit test They make me write a whole lot of extra code They’re slow Code First results in terrible data models The Developers insist on using them It doesn’t seem like developers ever test them They make me do whole lot of extra maintenance They’re slow
Mitigation Strategies
Mitigation Strategy #1: Pay Attention to Database Design Proper database design can only be done once – so do it right the first time Poor database design will result in poor query performance no matter the method of access – you will always be fighting the original design Design for the application’s access patterns – discard true relational patterns when it makes sense Avoid falling into an “entity-per-table” trap – use multiple tables per entity when it makes sense Avoid using “Code First” tools – they may accelerate the initial development, but will ultimately result in poor design choices
Mitigation Strategy #2: Encourage Proper Storage Techniques If the application calls for storing unstructured data or data that does not fit easily in SQL Server, consider other database systems It may be tempting to use the JSON or XML features of SQL Server to store unstructured data, but these structures can be difficult to query and make performant Images, PDFs and the like do not belong in the database – choose a proper storage medium and store a pointer to the file in the database
Mitigation Strategy #3: Log, Assess and Monitor Generated SQL Entity Framework, NHibernate and many other ORMs include the ability to log the generated SQL and other statistics Statistics can include time of execution, how long the query took and the actual SQL executed This log output can be sent to the diagnostics trace or actual log files During development, the generated SQL should be reviewed on a regular basis so problems can be detected early
Demo Time!
A Stored Procedure Solution
You Can Call Stored Procedures for Your Data Entity Framework, along with may other ORMs, allows you to call Stored Procedures and map the result to Entity objects (note we’re still logging off the Database Context):
You Can Even Call Stored Procedures With Parameters! In most ORMs, this is somewhat more complicated
But There’s a Small Problem… How many stored procedures do you want in your database? Who writes those? Who maintains those?
Introducing…A Different Kind of Stored Procedure We can utilize Dynamic SQL, coupled with sp_executesql, to create more flexible stored procedures The query plan is still cached (we’ll look at this in a minute) Full control over “generated” SQL Minimize parameter sniffing issues No implicit conversion (unless you want it) Pattern that is easy to extend without breaking existing code
And We Can Extend the Pattern to Multiple Tables!
Demo Time!
Dapper – A Micro ORM
What is a Micro ORM? A Micro ORM usually only provides a thin wrapper around data access and usually forgoes all but the most basic SQL generation The goal of a Micro ORM is to provide the Object-Relational Mapping functionality without “everything else”
What is Dapper? Dapper is a Micro ORM developed for use in Stack Exchange and its family of sites (otherwise known as the resource without which we couldn’t do our jobs) It is open-sourced at https://github.com/StackExchange/Dapper The primary goal of Dapper is to provide extremely fast performance across a variety of Relational Databases To achieve this, Dapper provides a set of extension methods on IDbConnection (the base ADO.NET connection interface in C#) that allow querying and mapping A sub-project also provides basic CRUD methods
Why Use Dapper? Provides full control over all SQL Except if the CRUD extensions are used Provides excellent performance – faster than every other data access method in C# except coding it all yourself Provides an easy way to provide parameters Since it extends IDbConnection, Dapper can be used with a wide range of Relational Databases Still automates mapping of query results to objects, including object graphs Allows the specification of ANSI or UNICODE strings
Demo Time!
Tool List Entity Framework - https://docs.microsoft.com/en-us/ef/ef6/index Dapper - https://github.com/StackExchange/Dapper Serilog - https://serilog.net/ Demo Code - https://github.com/danielmallott/orms-and-the-dba
Who am I? Dan Mallott Twitter: @DanielMallott Github: https://github.com/danielmallott LinkedIn: http://lnked.in/danmallott Principal for West Monroe Partners In the industry since 2011 Primary experience with SQL Server, starting with SQL Server 2005 Also worked with Oracle, PostgreSQL, and Cassandra Been both a DBA and a developer Have a couple Microsoft certifications DataStax Certified Professional
Questions?