Architecting Effective SQL Server Data Access Solutions
Download
Report
Transcript Architecting Effective SQL Server Data Access Solutions
Architecting Effective
SQL Server Data Access
Solutions
Adam Machanic
Database Consultant
Data Manipulation Group, Inc.
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
Type differences
Objects and relations are not compatible
paradigms
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
The Hierarchy
DATA LOGIC,
SOME
BUSINESS
LOGIC
BUSINESS
LOGIC
APP LOGIC
BUSINESS
LOGIC
BUSINESS
LOGIC
APP LOGIC
APP LOGIC
© 2006 Adam Machanic
ANY
LOGIC?
Architectural concepts: A
quick review
Coupling
Cohesion
How dependent is a module on another module it
uses?
Do your routines do exactly one thing?
Encapsulation
How much do your routines know about the
modules they use?
© 2006 Adam Machanic
Coupling
class 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
Cohesion
bool 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
Cohesion
bool 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
Encapsulation
bool 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
procedure
CREATE PROCEDURE myGreatProc
@ColumnList NVARCHAR(500)
AS
BEGIN
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
© 2006 Adam Machanic