APP8
APP8
APP8
Dapper
2023
Introduction
• Dapper is a Micro-ORM which helps to map plain query output to domain classes.
• It can run plain SQL queries with great performance.
• Dapper is a lightweight framework that supports all major databases like SQLite, Firebird, Oracle, MySQL,
and SQL Server.
• It does not have database-specific implementation.
• Dapper is built by the StackOverflow team and released as open source.
Create a Class Library
http://www.authorcode.com/creating-a-class-library-with-c-and-net-core-in-visual-studio-code/
Traditional ORM [Entity Framework] vs Micro ORM
Traditional ORM [Entity Framework] vs Micro ORM
https://gist.github.com/brunobmo/df1c0096080dbb4eac2c8bbc9afed199
Create a Class Library
• Add to the appsettings.json file the connection string to access your SQL Server database:
"ConnectionStrings": {
"Default": ”(...)"
}
SqlDataAcess
• Let’s create a class to encapsulate the logic behind configuring acess to database:
• The purpose of this class, SqlDataAccess, is to provide a means of accessing a SQL database and
performing data operations.
• It implements the ISqlDataAccess interface, indicating that it adheres to a specific contract defined by
that interface.
SqlDataAcess
https://gist.github.com/brunobmo/6400ce751f23c1947bd1bb9c0d610992
SqlDataAcess
namespace DataLayer;
https://gist.github.com/brunobmo/6d4d56757b5ea1b44b6f3793b469da47
SqlDataAcess
namespace DataLayer; The CardModel.cs class is a Data Transfer Object (DTO) since it
public record CardModel is primarily used for transferring data between the class
{ library and the Blazor app.
public string? _Id { get; set; }
public int Id { get; set; }
public string Name { get; set; } = "";
public string Description { get; set; } = "";
public string Image { get; set; } = "";
public DateTime? Date { get; set; }
public int TimeInMinutes { get; set; }
public UserModel? Owner { get; set; } = new UserModel();
https://gist.github.com/brunobmo/1047be85df7935f796f9198c9eaf1495
SqlDataAcess
namespace DataLayer;
https://gist.github.com/brunobmo/f1a3baec1cf929c51f0535ddfa48aa4c
SqlDataAcess
namespace DataLayer;
public record UserModel
{
int Id { get;}
string _Id { get; set;} = "";
string Name { get; set;} = "";
string Email { get; set;} = "";
}
https://gist.github.com/brunobmo/ce3e780fce276c836bd9ba30d3a77070
Repository Skeleton
https://medium.com/@pererikbergman/repository-design-pattern-e28c0f3e4a30
Repository Skeleton
Task<List<CardModel>> FindAll();
https://gist.github.com/brunobmo/5fd3a4728161e20c73ffa738f0d65496
Repository Skeleton
• Let’s implement the class with, for now, the (limited) findAll behavior:
namespace DataLayer;
public class CardRepository : ICardRepository{
private ISqlDataAccess _db;
public CardRepository(ISqlDataAccess db){
_db = db;
}
public Task<CardModel> Find(int id){
throw new NotImplementedException();
}
public Task<List<CardModel>> FindAll(){
string sql = "select * from Cards";
return _db.LoadData<CardModel, dynamic>(sql, new { });
}
(...)
https://gist.github.com/brunobmo/d18a372636ac170caf8bbb71897a8c44
Configure main app – Program.cs
builder.Services.AddTransient<ISqlDataAccess, SqlDataAccess>();
builder.Services.AddTransient<ICardRepository, CardRepository>();
@code {
private List<DataLayer.CardModel> cards;
https://gist.github.com/brunobmo/511d69489d69ad4b3d6ded9cafd1d02f
Adding data
https://gist.github.com/brunobmo/7021ac189b3fc2f66be4261c3431e59f
Update Data
• Modify FectData.razor and test FindAll, Find, Add, Update for simple objects.
• Implement the Remove.
https://gist.github.com/brunobmo/4b33ecdfc9a13047de7390557d877ade
Using Relationships With Dapper
• Notice that we don’t have the Owner data or the Tags data.
• Dapper provides a feature called Multi mapping to map data to multiple objects explicitly and nested
objects.
• The splitOn parameter tells Dapper what column(s) to use to split the data into multiple objects.
https://www.learndapper.com/relationships
Using Relationships With Dapper - One to Many
• Let’s add LoadData to SqlDataAccess and ISqlDataAccess for loading Cards and the User.
<T1, T2, U>: These are generic type parameters. They allow the
Task<List<T1>> LoadData<T1, T2, U>( method to work with different types without specifying them directly.
string sql, T1 and T2 represent the types of objects that the method will operate
U parameters, on, while U represents the type of the parameters parameter.
Func<T1, T2, T1> mappingFunction,
string splitOn,
Func<T1, object> groupByFunc = null,
Func<IGrouping<object, T1>, T1> selectFunc = null
);
https://gist.github.com/brunobmo/442633482221029c6f6a4dec2d5bf866
Using Relationships With Dapper Many to Many
• The multi-mapping works with multiple relationships. we are loading Cards, Tags and User
public async Task<List<CardModel>> FindAll(){
string sql = @"SELECT a.id, a.name, description, image, date, time_in_minutes, u.id,
u.name, u.email, c.id, c.name
FROM [dbo].[cards] AS a LEFT JOIN dbo.card_tags AS b ON a.[id]=b.id_card
LEFT JOIN dbo.tags AS c ON b.id_tag = c.id
LEFT JOIN dbo.users AS u ON u.id = a.[owner]";
return await _db.LoadData<CardModel, UserModel , TagModel, dynamic>(sql,new { },(card,
owner, tag) =>{
card.Owner= owner; specifying the column names to be used
card.Tags.Add(tag); for grouping the results.
return card;},
"id, id", specifies how to extract the ID from a
p => p.Id, grouped set of records.
g =>{ responsible for grouping the
var groupedCard = g.First(); cards and tags based on the
groupedCard.Tags = g.Select(p => p.Tags.Single()).ToList(); specified column names
return groupedCard;
});
}
https://gist.github.com/brunobmo/b3d589410f220d3bdc13fe5821c35500
Using Relationships With Dapper Many to Many
https://gist.github.com/brunobmo/52f3f7a4642c60942d06deb74b8c66cd
Updating Find (one) using Multiple results
• The Dapper QueryMultiple method allows you to select multiple results from a database query.
• That feature is very useful for selecting multiple result sets at once, thus avoiding unnecessary round trips to
the database server.
• Let’s add a new method to SqlDataAccess and respective interface in ISqlDataAccess :
public async Task<Tuple<T1, List<T2>, T3>> LoadData<T1 , T2, T3, U>(string sql, U parameters){
string? connectionString = _config.GetConnectionString(ConnectionStringName);
}
return null;
}
https://gist.github.com/brunobmo/52f3f7a4642c60942d06deb74b8c66cd
Updating Find (one) using Multiple results
• Let’s change Find method in CardRepository to get Card, User and Tags using QueryMultiple method
public async Task<CardModel> Find(int id){
string sql =
"SELECT * FROM Users WHERE id = (SELECT owner FROM Cards WHERE id=@Id);"
+ "SELECT * FROM Card_Tags WHERE id_card = @Id;"
+ "SELECT id,name,description,image,date, time_in_minutes FROM Cards WHERE id = @Id;";
https://gist.github.com/brunobmo/5bdf88d034754032c8e6b73a6a288b67
Create-update-delete with complex objects
• Add method will deal with simple objects while save method will deal with complex objects
• First we are going to create a new field in CardModel and Tag class (we are assuming the card owner
always exist and was not changed in this context):
• In TagModel we are adding IsNew and IsDeleted properties to control when Tags are added or removed
from a card
namespace DataLayer;
https://gist.github.com/brunobmo/2516ddcd30261ff8f93607e31f331a67
Create-update-delete with complex objects
• TagRepository
namespace DataLayer;
• Add method
public async Task<TagModel> Add(TagModel tag)
{
string sql =
@"INSERT INTO dbo.tags(name) VALUES(@name);" +
"SELECT CAST(SCOPE_IDENTITY() as int)";
int id = await _db.SaveData<int, TagModel>(sql, tag);
return new TagModel() { Id = id, Name = tag.Name };
}
• Update
• Delete method
public async Task Delete(TagModel tag, CardModel car)
{
string sql = "DELETE FROM dbo.card_tags WHERE id_card = @id_card AND id_tag = @id_tag";
await _db.SaveData<dynamic>(sql, new { id_card = car.Id, id_tag = tag.Id });
} Ensure you have ON
DELETE CASCADE in the
FK
• Find method
https://gist.github.com/brunobmo/c2e61edca8d1ccdf32d703fe7c7f6b14
Create-update-delete with complex objects
https://gist.github.com/brunobmo/338a5c5dbcd1ae31686d30c674b2fc4b
Create-update-delete with complex objects
• Everything may have worked just now, but remember, we're updating multiple rows here.
• So we need to wrap this method in a transaction in case anything goes wrong.
• Let's use a TransactionScope for this.
https://gist.github.com/brunobmo/3d9988eff989ff8475819654caca517b
Create-update-delete with complex objects
• Dapper is a Micro-ORM which helps to map plain query output to domain classes.
• It can run plain SQL queries with great performance.
• A class library is used for reusability and separation of concerns;
• A DataAcess library can be used to separate application logic and data layer
Summary
• The repository pattern provides good separation of concerns, easy mocking, and good encapsulation.
• Dapper provides several ways for mapping objects, QueryMultiple is one of those methods
Blazor Server Course
Dapper
2023