.NET Database Toolkit

22
.NET Database Toolkit Dapper, Spritely.Cqrs, AutoMapper, FluentMigrator

Transcript of .NET Database Toolkit

Page 1: .NET Database Toolkit

.NET Database ToolkitDapper, Spritely.Cqrs, AutoMapper, FluentMigrator

Page 2: .NET Database Toolkit

What is Dapper?

A “micro-ORM” written by these guys…

Sam Saffron Marc Gravell

Page 3: .NET Database Toolkit

What is an ORM?

From Wikipedia: Object-relational mapping (ORM, O/RM, and O/R mapping) in

computer science is a programming technique for converting data between

incompatible type systems in object-oriented programming languages.

Challenges

There are a variety of difficulties that arise when considering how to match an object

system to a relational database.

An alternative to implementing ORM is use of the native procedural languages provided

with every major database on the market. The Data Access Object (DAO) design pattern

is used to abstract these statements and offer a lightweight object-oriented interface to

the rest of the application

Examples

Entity Framework

NHibernate

Page 4: .NET Database Toolkit

What is CQ[R]S?

From Wikipedia: Command–query separation (CQS) is a principle of imperative computer programming. It was devised by Bertrand Meyer as part of his pioneering work on the Eiffel programming language.

It states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both. In other words, Asking a question should not change the answer. More formally, methods should return a value only if they are referentially transparent and hence possess no side effects.

Offers:

Easier scaling (easy to manage read replicas).

Easier testing (easy to mock out each call explicitly).

I’m using interfaces from NuGet package: Spritely.Cqrs.

The “query” or “command” class is the parameter object (can have zero properties).

The handler class has the behavior and is what gets mocked during testing.

Page 5: .NET Database Toolkit

// arrangevar expectedThing = new ThingDataAccessObject() { Id = 0, Values = "Monkey" };var thingMap = new Dictionary<int, ThingDataAccessObject> {

{ expectedThing.Id, expectedThing } };var mockGetThingByIdQueryHandler = new

Mock<IQueryHandler<GetThingByIdQuery, ThingDataAccessObject>>();mockGetThingByIdQueryHandler

.Setup(_ => _.Handle(It.IsAny<GetThingByIdQuery>()))

.Returns<GetThingByIdQuery>(query => thingMap[query.ThingId]);var service = new ThingWcfService(mockGetThingByIdQueryHandler.Object);

// actvar actualThing = service.GetThing(expectedThing.Id);

// assertAssert.Equal(expectedThing.ToThingWcfObject().Id, actualThing.Id);Assert.Equal(expectedThing.ToThingWcfObject().Values, actualThing.Values);

// - make sure we're testing all of the propertiesAssert.Equal(2, typeof(ThingWcfObject).GetProperties().Count());

Testing with CQRS

Page 6: .NET Database Toolkit

Ok, what is Dapper?

From Wikipedia: Dapper was started by Sam Saffron and Marc

Gravell because of the N+1 and other performance issues with Entity

framework. Dapper was written for and is used by Stack Overflow.

Dapper's primary feature is mapping from .NET classes to database tables

(and from CLR data types to SQL data types). Dapper also provides data query

and retrieval facilities.

Dapper is a micro-ORM: it does not offer the full range of features of a full

ORM such as nHibernate or Entity Framework. This is by design. Dapper does

not generate the SQL queries, but only maps the result to Plain Old CLR

Objects (POCOs).

The single class ORM is also available on NuGet.

Page 7: .NET Database Toolkit

What about performance?

Page 8: .NET Database Toolkit

ADO.Net vs. Dapper – First the CQRS Part

public class GetThingByIdQuery : IQuery<ThingDataAccessObject>{

public int ThingId { get; set; }}

public class GetThingByIdQueryHandler : IQueryHandler<GetThingByIdQuery, ThingDataAccessObject>

{private readonly string connectionString;

public GetThingByIdQueryHandler(string connectionstring){

this.connectionString = connectionstring;}

public ThingDataAccessObject Handle(GetThingByIdQuery query){

Basic CQRS…

• Class for params (the ‘Query’)

• Class for behavior (the

‘QueryHandler’)

Page 9: .NET Database Toolkit

ADO.Net vs. Dapper – What they share

Pretty standard fare…

• Using SQL is a good thing because

you can optimize for the database

engine’s provided advantages.

• Use parameterized SQL to prevent

injection…

ThingDataAccessObject ret = null;var sqlText = @"SELECT ThingId as [Id],

[Values] as [Values] FROM Thing WHERE ThingId = @ThingId";

using (IDbConnection connection = new SqlConnection(this.connectionString)){

connection.Open();

Page 10: .NET Database Toolkit

ADO.Net Version

Really not that bad…

• It is a little bit of boiler plate

code though which is a pain

var command = connection.CreateCommand();command.CommandType = CommandType.Text;command.CommandText = sqlText;command.Parameters.Add(new SqlParameter("ThingId", query.ThingId));using (var reader = command.ExecuteReader()){

reader.Read();ret = new ThingDataAccessObject

{Id = reader.GetInt32(0), Values = reader.GetString(1)

};}

Page 11: .NET Database Toolkit

Dapper Version

This should really sell itself…

• Dapper is just that easy!

ret = connection.Query<ThingDataAccessObject>(sqlText, query).SingleOrDefault();

Page 12: .NET Database Toolkit

What is AutoMapper?

AutoMapper is a package on Nuget (Id=AutoMapper)

A convention-based object-object mapper.

Uses a fluent configuration API to define an object-object mapping strategy

and uses a convention-based matching algorithm to match up source to

destination values.

Geared towards model projection scenarios to flatten complex object models

to DTOs and other simple objects, whose design is better suited for

serialization, communication, messaging, or simply an anti-corruption layer

between the domain and application layer.

Page 13: .NET Database Toolkit

So you have this in your world…

• This is normal!

• Don’t ever just use only the

EntityFramework class in all

layers no matter what anyone

tells you!

• These objects are contracts in

their own systems.

public class ThingDatabaseObject{

public int Id { get; set; }public string Values { get; set; }

}

public class ThingDataAccessObject{

public int Id { get; set; }public string Values { get; set; }

}

public class ThingWcfObject{

public int Id { get; set; }public string Values { get; set; }

}

public class ThingRestObject{

public int Id { get; set; }public string Values { get; set; }

}

Page 14: .NET Database Toolkit

So you have this in your world…

• This is actually a pretty high overhead, but historically kind of a necessary evil…

public static ThingDataAccessObject ToDataAccessObject(this ThingDatabaseObject source){

return new ThingDataAccessObject { Id = source.Id, Values = source.Values };}

public static ThingWcfObject ToThingWcfObject(this ThingDataAccessObject source){

return new ThingWcfObject { Id = source.Id, Values = source.Values };}

public static ThingRestObject ToThingRestObject(this ThingDataAccessObject source){

return new ThingRestObject { Id = source.Id, Values = source.Values };}

Page 15: .NET Database Toolkit

And you aslo have this in your world…

• These tests add to the high overhead…

public class ThingConversionExtensionMethodsTests{

[Fact]public static void ToDataAccessObject_InputAllPropsSet_OutputAllPropsMatch(){

// arrangevar objectIn = new ThingDatabaseObject() { Id = 4, Values = "SomeValues" };

// actvar objectOut = objectIn.ToDataAccessObject();

// assertAssert.Equal(objectIn.Id, objectOut.Id);Assert.Equal(objectIn.Values, objectOut.Values);

// - make sure we know all the properties to testAssert.Equal(2, typeof(ThingDatabaseObject).GetProperties().Count());Assert.Equal(2, typeof(ThingWcfObject).GetProperties().Count());

}

Page 16: .NET Database Toolkit

Mapper.CreateMap<ThingDataAccessObject, ThingWcfObject>();Mapper.AssertConfigurationIsValid();

NOW you have this in your world…

• This is better!

• Easy overhead and if you need special mappings

there is a ton of extensibility and all in one place…

Page 17: .NET Database Toolkit

There’s really not much difference…

• There is a little overhead on use with

AutoMapper.

• There is much more overhead with extension

methods to convert.

• The extension method version reads a little

nicer

• The AutoMapper version wins just for speed of

coding…

public class ThingWcfService{

static ThingWcfService(){

RunDiagnostics();}

public static void RunDiagnostics(){

Mapper.CreateMap<ThingDataAccessObject, ThingWcfObject>();Mapper.AssertConfigurationIsValid();

}

public ThingWcfObject GetThing(int id){

var dao = ThingDataAccess.GetThingById(id);

var extMethodRet = dao.ToThingWcfObject();var autoMapperRet = Mapper.Map<ThingWcfObject>(dao);

Page 18: .NET Database Toolkit

What is Fluent Migrator?

A NuGet package that allows you manage your database migrations in .NET

source control with supported tooling.

Modeled after the Ruby style of database migrations.

You need this because otherwise you’re probably in one of these camps:

We just re-script from production because we have no DB source we trust.

We suffer EntityFramework because then we always have DB source checked in.

We have some custom complicated process of keeping alter scripts in a structure.

Page 19: .NET Database Toolkit

Each Migration is a class

[Migration(Versions.BaseMigrationVersion)]public class ThingDatabaseInitialMigration : Migration{

public class Versions{

public const long BaseMigrationVersion = 0;}

Create a class like so…

• I use a class to hold the versions used in the

attribute as a convenience.

Page 20: .NET Database Toolkit

Migrations have Migrate “Up” and “Down”

Perform your database operations…

• The fluent grammar makes it really nice to

declare items, add seed data, remove

unneeded items, and easy to port to other

Database Types.

• Can also just run raw SQL like for creating

StoredProcedures.

public static ThingDatabaseObject SeedData = new ThingDatabaseObject() { Id = 0, Values = "Monkey" };

public override void Up(){

Create.Table("Thing").WithColumn("ThingId").AsInt32().PrimaryKey().WithColumn("Values").AsString();

Insert.IntoTable("Thing").Row(SeedData);}

public override void Down(){

Delete.Table("WorkingTranslate_v1");}

Page 21: .NET Database Toolkit

Easy to Execute

Naos.Database.Migrator.MigrationExecutor.Up(typeof(ThingDatabaseInitialMigration).Assembly,connectionString,dbName,ThingDatabaseInitialMigration.Versions.BaseMigrationVersion,Console.WriteLine);

Run through the runner…

• There is a command line exe that ships native

with the FluentMigrator package in NuGet.

• Naos.Database.Migrator is a thin wrapper

around the FluentMigrator Protocol and

available in NuGet.

• Can also execute in preview mode and just

show the SQL to apply.

Page 22: .NET Database Toolkit

Links Author: Lawson Caudill – http://www.getthinktank.com

Code in deck is available at – https://github.com/wlscaudill/Presentation---

.NET-Database-Toolkit

https://github.com/StackExchange/dapper-dot-net

http://samsaffron.com

http://marcgravell.blogspot.com/

http://en.wikipedia.org/wiki/Object-relational_mapping

https://www.nuget.org/packages/Spritely.Cqrs/

https://www.nuget.org/packages/AutoMapper/

https://github.com/schambers/fluentmigrator

https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92