architecting effective sql server data access solutions adam machanic database consultant data...
TRANSCRIPT
Architecting Effective SQL Server Data Access
Solutions
Adam Machanic
Database Consultant
Data Manipulation Group, Inc.
© 2006 Adam Machanic
About Adam Machanic
Independent Software Consultant SQL Server and .NET Specialist
SQL Server MVP Co-author, Pro SQL Server 2005 (Apress) SQL Server 2005 “Expert”,
www.SearchSQLServer.com [email protected]
© 2006 Adam Machanic
Data Access == A Big Headache
Implementation differences Objects and relations are not compatible
paradigms Type differences
SQL Server types and .NET types are not aligned Build and deployment mismatch
The processes used to “compile” application and database are totally different
© 2006 Adam Machanic
Segmentation of Logic
First problem: Too much intermingling of logic
Where does your logic really belong?
First, define what we’re dealing with: Data Logic Business Logic Application/UI Logic
© 2006 Adam Machanic
Data Logic
Rules for data consistency and correctness Keys, constraints, etc. Data-tier validation
Data rules are often codified business rules! The database must be able to enforce any and all
rules necessary to ensure that the data is correct Example rule: Accounts without overdraft
protection cannot go into the red
© 2006 Adam Machanic
Business Logic
Business logic dictates what to do with the data, and when to do it
How to transform the data in order to meet business requirements
How to manipulate the data to make business decisions (i.e., how to turn data into knowledge)
© 2006 Adam Machanic
Application Logic
User interface behaviors
String/numeric/date formatting rules
Localization
Anything that has more to do with displaying data than actually manipulating it
© 2006 Adam Machanic
APP LOGIC
APP LOGIC
APP LOGIC
The Hierarchy
DATA LOGIC,
SOME BUSINESS LOGIC
BUSINESS LOGIC
BUSINESS LOGIC
BUSINESS LOGIC
ANY LOGIC?
© 2006 Adam Machanic
Architectural concepts: A quick review
Coupling How dependent is a module on another module it
uses?
Cohesion Do your routines do exactly one thing?
Encapsulation How much do your routines know about the
modules they use?
© 2006 Adam Machanic
Couplingclass Dealership{ //name of the dealership string Name; //owner of the dealership string Owner; //cars that the dealership has Car[] Cars;
//defining the Car subclass class Car { //make of the car string Make; //model of the car string Model; }}
class CarOwner{ //name of the car owner string name;
//the owner’s cars Dealership.Car[] Cars}
TOO MUCH DEPENDENCY == TIGHT COUPLING
© 2006 Adam Machanic
Cohesionbool TransferFunds( Account AccountFrom, Account AccountTo, decimal Amount){ if (AccountFrom.Balance >= Amount) AccountFrom.Balance -= Amount; else return(false);
AccountTo.Balance += Amount;
return(true);}
WHY DOES “TRANSFER” KNOW HOW TO DEPOSIT AND WITHDRAW?
© 2006 Adam Machanic
Cohesionbool TransferFunds( Account AccountFrom, Account AccountTo, decimal Amount){ bool success = false; success = Withdraw(AccountFrom, Amount);
if (!success) return(false);
success = Deposit(AccountTo, Amount);
if (!success) return(false); else return(true);}
© 2006 Adam Machanic
Encapsulationbool Withdraw( Account AccountFrom, decimal Amount){ if (AccountFrom.Balance >= Amount) { AccountFrom.Balance -= Amount; return(true); } else return(false);}
SHOULD “WITHDRAW” HANDLE THESE ESSENTIAL RULES FOR THE ACCOUNT CLASS?
© 2006 Adam Machanic
Encapsulation
bool Withdraw( Account AccountFrom, decimal Amount){ return (Account.Withdraw(Amount));}
LET THE CLASS HANDLE ITS OWN RULES!
© 2006 Adam Machanic
What does this have to do with data access?
Data access routines are just like any other routines
Application developers sometimes forget the rules when it comes to data access
Why can’t the database itself be treated just like an object?
© 2006 Adam Machanic
The Key: Data Interfaces (stored procedures!)
Properly designed stored procedures provide… Encapsulation of data and schema … which leads to loose coupling between the
application and the database… … which leads to much more cohesive
application-level code…
If the database is thought of as an object, stored procedures are nothing but methods!
© 2006 Adam Machanic
Interfaces
Stable, encapsulated access to underlying resources
Consistent inputs, consistent outputs
Defines a “contract” Stored procedures define “implicit” contracts – not
enforceable, but still testable
© 2006 Adam Machanic
The Database as API Mindset
Application sees the database as a black box
Stored procedures become cohesive “action” interfaces Get(XXXX) Update(XXXX) Delete(XXXX)
Have a database problem? No need to fix the application.
© 2006 Adam Machanic
The Interface Balance
Testability
Maintainability
Security
Performance
Creeping Featurism
© 2006 Adam Machanic
Testability
Avoid polymorphic interfaces
“Flexible” is a bad thing when it comes to testing
Partition functionality – avoid combinatorial explosions (think about cohesion)
© 2006 Adam Machanic
Maintainability
Maintainability is concerned with both interfaces and actual code
“Flexible” may mean less maintainable Flexible interfaces may tightly couple the caller to
the routine Flexible code may be more complex
Tie-in with testability: The easier a routine is to test, the easier it will be to maintain!
© 2006 Adam Machanic
A problematic stored procedureCREATE PROCEDURE myGreatProc @ColumnList NVARCHAR(500)ASBEGIN SET NOCOUNT ON
DECLARE @sql NVARCHAR(2000) SET @sql = 'SELECT ' + @ColumnList + 'FROM myTable ‘
EXEC (@sql)END
How many possible combinations of columns can be passed in?
What if they’re passed in a different order?
What if the column names in the database change?
Does not returning all of the columns every time really make that much of a difference?
© 2006 Adam Machanic
Security
Complex security schemes can make testing and maintenance more difficult (and expensive) How many test combinations are required to test
all authentication schemes? Can you be sure that both sides of the security
question are covered—what you can access as well as what you can’t?
© 2006 Adam Machanic
Most important (and easily fixed) security question: Injection!
Always, always, always parameterize queries! .NET: Use Parameters collection SQL Server: Use sp_executesql if you’re using
dynamic SQL
© 2006 Adam Machanic
Performance
Performance can sometimes have greater value than correctness
More security can often mean lower performance
Simpler, more testable/maintainable modules often tend to exhibit better performance!
Encapsulating access within a stored procedure often makes it easier to fix performance issues
© 2006 Adam Machanic
Performance impact: Chatty interfaces
Try to merge multiple calls into a single call
Remember: Scaling the application or Web server is often cheaper than scaling the SQL Server!
© 2006 Adam Machanic
Creeping Featurism
Do you have properties, methods, or objects marked, “Reserved for Future Use”?
Does adding unused complexity upfront help later – or simply add to increased maintenance cost in the interim?
Adding features tomorrow should be a secondary concern to producing working software today.
© 2006 Adam Machanic
Other interface hints
Use column aliases to help keep interfaces consistent even when schemas change
SELECT
ProductNo
FROM Products
SELECT
ProductNumber AS ProductNo
FROM Products
© 2006 Adam Machanic
Other interface hints
Optional parameters? Dynamic SQL is your friend!
CREATE PROCEDURE GetMyData1
@Param1 INT
AS
…
CREATE PROCEDURE GetMyData2
@Param2 INT
AS
…CREATE PROCEDURE GetMyData3
@Param1 INT,
@Param2 INT
AS
…
© 2006 Adam Machanic
Dynamic SQL – PARAMETERIZED
DECLARE @sql NVARCHAR(100)
SET @sql = 'SELECT ColA, ColB, ColC ' + 'FROM YourTable ' + 'WHERE 1=1 ' + CASE WHEN @Param1 IS NOT NULL THEN 'AND YourCol1 = @Param1 ‘ ELSE '' END + CASE WHEN @Param2 IS NOT NULL THEN 'AND YourCol2 = @Param2 ‘ ELSE '' END
EXEC sp_executesql @sql, N'@Param1 INT, @Param2 INT', @Param1, @Param2
© 2006 Adam Machanic
Other interface hints
Avoid use of SELECT * in stored procedures
Do you really need a dynamic table name?
The CASE expression is your friend; use it when you need to change output order, column calculations, or anything else dynamic
© 2006 Adam Machanic
Summary
Think of the database as a shared resource that the application happens to use
Design encapsulation into your data access methodology
Avoid the temptation to make things overly “flexible”
Consistent interfaces lead to inexpensive maintenance going forward