Developer
Forum


Dapper
Refit

Serilog
Dotnet cli

Serilog
Flexible, structured events — log file convenience.
Serilog api
Clean and modern
Code-as-configuration
Simple 'levelled' logging
Thread-safe
Highly concurrent
async aware
No default ambient state
What the difference
- Event capturing with message templates
- Correlation through Event enrichment
Message templates
Text Logging
Structured logging
Log($"Commit took {0} ms", elapsed);Log(new {
Event = "commit",
Elapsed = elapsed
});- Easy to write
- Easy to read logs
- Loss of information
- Hard to parse by machine
- Cumbersome to write
- Hard to read logs
- Easy to parse
Demo
Message templates
log.information("Processed {@sensorInput} in {Elapsed} ms", sensorInput, elapsed);MessageTemplate
Timestamp
SensorInput
Elapsed
SensorIProcessed {@sensorInput} in {Elapsed} msnput
2018-03-02 12:00:00.123
24.7
Lat
Long
132.2
153
[2018 ...] Processed at Lat: 24.7, Long 132.2 in 153 ms
json.log
ELASTIC
correlation
Syntetic correlation
Natural correlation
CorrelationId
RequestId
ThreadId
MachineName
ProcessName
Application
EmployeeId
RequestPath
Url
...
correlation
When searching through logs we use different correlation to find the IMPORTANT logs.
Origination request id to search over multiple service boundaries
Logs grouped by request
...
Enrichment api
| Examples | Api | |
|---|---|---|
| Fixed values | App Name Environment Version |
Enrich.WithProperty("App", "Demo") |
| Ambient state | ThreadId ProcessId Http State |
Enrich.WithThreadId() |
| Scoped state | MessageId OrderId |
Enrich.FromLogContext() LogContext.PushProperty("App", "Demo") |
| Local values | SourceContext | Log.ForContext<Homecontroller>() |
Dapper
Dapper - a simple object mapper for .Net
And it is really fast!
What does dapper give us?
- Sql injection safe parameters
- Handles parameter list out of the box
- Maps SqlDataReader to C# types
- And did I say that it is fast
- Visible sql to easily test in SqlManagementStudio
- Use Sql Table-Valued parameters
Parameters
string sql = @"
INSERT INTO Customers (CustomerName)
Values (@CustomerName);";
using (var connection = new SqlConnection(cs))
{
connection.Open();
var affectedRows = connection.Execute(
sql,
new {CustomerName = "Mark"}
);
Console.WriteLine(affectedRows);
}Anonymous parameter class
Parameters
var sql = "Invoice_Insert";
using (var connection = My.ConnectionFactory())
{
connection.Open();
DynamicParameters parameter = new DynamicParameters();
parameter.Add("@Code", "Many_Insert_0", DbType.String, ParameterDirection.Input);
parameter.Add("@RowCount", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);
connection.Execute(sql,
parameter,
commandType: CommandType.StoredProcedure);
int rowCount = parameter.Get<int>("@RowCount");
}DynamicParameters
Parameters
var sql = "SELECT * FROM Invoice WHERE Id IN @Ids;";
using (var connection = My.ConnectionFactory())
{
connection.Open();
var invoices = connection.Query<Invoice>(
sql,
new {Ids = new[] { 1, 2 }}
).ToList();
}List
Parameters
public class JobCollectiveBargainRepository : IJobCollectiveBargainRepository
{
private const string typeName = "COLLECTIVEBARGAIN_ARTICLE";
private static readonly SqlMetaData[] ArticleRow = {
new SqlMetaData("BisUniqueId", SqlDbType.BigInt),
new SqlMetaData("PayAmount", SqlDbType.Decimal, 10,2),
new SqlMetaData("PayPercent", SqlDbType.Decimal, 5,2),
};
public async Task UpdateArticles(List<Article> artList) {
var parameters = new { articles = artList.Select(CreateRow)
.AsTableValuedParameter(typeName) };
using (var conn = new SqlConnection(cs)) {
await context.ExecuteAsync(
storeProcName,
parameters,
commandType: CommandType.StoredProcedure);
}
}
private static SqlDataRecord CreateRow(Article x)
{
var row = new SqlDataRecord(ArticleRow);
row.SetInt64(0, x.BisUniqueId);
row.SetDecimal(1, x.PayAmount);
row.SetDecimal(2, x.PayPercent);
return row;
}
}TABLE-VALUED
REsult
public class Article {
public int Id { get; set; }
public string Name { get; set; }
}
public async Task<IEnumerable<Article>> GetArticles() {
using (var conn = new SqlConnection(cs)) {
await context.QueryAsync<Article>(
"SELECT ArticleId as Id, Title as Name FROM Article"
);
}
}Strongly typed
REsult
public static string SqlGetRecordByUrlUniqueId = @"
SELECT r.[Id]
...
FROM [dbo].[GflRecord] as r
WHERE r.UrlUniqueId = @UrlUniqueId
SELECT si.[Id] AS Id
...
FROM [dbo].[GflRecord] as r
INNER JOIN [dbo].[SalaryInformation] as si on r.fkSalaryInformationId = si.Id
WHERE r.UrlUniqueId = @UrlUniqueId
SELECT ci.[Id] AS Id
...
FROM [dbo].[GflRecord] as r
INNER JOIN [dbo].[ClientInformation] as ci on r.fkClientInformationId = ci.Id
WHERE r.UrlUniqueId = @UrlUniqueId
";
using (var conn = new SqlConnection(_config.ConnectionString)) {
var grid = await conn.QueryMultipleAsync(
SqlBuilder.SqlGetRecordByUrlUniqueId,
new { UrlUniqueId = urlUniqueId });
var record = (await grid.ReadAsync<GflRecordSql>()).SingleOrDefault();
var salary = (await grid.ReadAsync<GflSalarySql>()).SingleOrDefault();
var client = (await grid.ReadAsync<GflClientSql>()).SingleOrDefault();
return record?.ToModel(salary, client);
}
Multi result
Multiple Resultsets
string sql = @"
SELECT * FROM Invoice WHERE InvoiceID = @InvoiceID;
SELECT * FROM InvoiceItem WHERE InvoiceID = @InvoiceID;
";
using (var connection = My.ConnectionFactory())
{
connection.Open();
using (var multi = connection.QueryMultiple(sql, new {InvoiceID = 1}))
{
var invoice = multi.Read<Invoice>().First();
var invoiceItems = multi.Read<InvoiceItem>().ToList();
}
}List
REsult
Multi mapping
Come talk to me
Multi type
Come talk to me
Buffering
Dapper buffers result by default
(speed for small resultsets)
For large resultset 10000 - Gb of data
turning off Buffering can help with memory issues
connection.Query<OrderDetail>(sql, buffered: false)Refit
The automatic type-safe REST library for .NET Core, Xamarin and .NET
What is refit
Strongly typed rest client
Uses HttpClient
Declare rest api contract via c# interface
Defining a rest interface
public interface IAdvertApiV3
{
[Get("/api/v3/adverts/jobref/{jobref}")]
Task<IEnumerable<Models.Response.Advert>> GetByJobRef(long jobref);
[Get("/api/v3/adverts/{advertRef}")]
Task<Models.Response.Advert> GetByAdvertRef(long advertRef);
[Get("/api/v3/adverts/latest/{count}")]
Task<List<Models.Response.Advert>> GetLatest(int count);
[Post("/api/v3/adverts/jobrefs")]
Task<IEnumerable<Models.Response.Advert>> PostByJobRef([Body]long[] jobRef);
[Post("/api/v3/adverts/advertrefs")]
Task<IEnumerable<Models.Response.Advert>> PostByAdvertRef([Body]long[] advertRefs);
[Get("/api/v3/adverts")]
Task<IEnumerable<long>> GetChangedAdvertsSince([Query] DateTime date);
[Post("/api/v3/adverts")]
Task<long> Post([Body]CreateAdvertRequest advert);
}Getting a Client
// DEPENDENCY INJECTION
var tokenCache = new TokenCache();
var apiConf = new ApiConfigurationService(config);
var refitFactory = new RefitFactory.RefitFactory(
apiConf,
tokenCache,
new HttpContextAccessor()
);
// Api s
services.AddTransient(x =>
refitFactory.GetRestService<IMailApi>("MailApi")
);USING A CLIENT
namespace AcademicWork.Gfl.Api.Controllers.Api
{
[Produces("application/json")]
[Route("api/gfl")]
public class GflController : Controller
{
private readonly IMailService _mailService;
public GflController( IMailService mailService)
{
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
_mailService = mailService;
}
[HttpPost()]
public async Task<IActionResult> Post([FromBody] CreateGflRequest input)
{
var body = await _mailService.RenderBodyAsync(
input.MailType,
input.Sender,
input.UrlUniqueId.ToUrl(),
input.LoginKey
);
return Ok(data.ToResponse(body));
}
}Refit Gotchas
http://localhost/api/values?id=1&id=2
Very hard to get to work
"Work around" or better api designs
http://localhost/api/values?ids=1,2
(Requires pluming in MVC, come see me)
Or just use POST (Or Rickards user will bite U)
Refit Gotchas 2
YOU MUST HAVE A DIRECT
REFERENCE TO REFIT IN THE
SAME PROJECT AS YOU ARE GOING TO USE IT!
Refit generate a class when you compile that is used as the concrete HttpClient!

Dotnet cli
AcademicWork Templates
Best Practices
Repository CI/CD setup
Environment setup (not yet)
Developer
By fhelje
Developer
- 474