- AspNet.Mvc
- AspNet.WebApi
- Common.Api
- Common.Exceptions
- Common.Extensions
- Common.Helpers
- Common.Jobs
- Common.Logon
- Common.Mail
- Common.MethodMiddleware
- Common.Smtp
- Common.Utils
- Common.Validation
- EntityFramework
- MrAdvice
- Newtonsoft.Json
- RazorEngine
HttpStatusCodeResult
with custom error message in response body.
public class HttpCustomErrorResult : HttpStatusCodeResult
{
public HttpCustomErrorResult(HttpStatusCode code, string description);
}
ActionResult
for (307 Temporary Redirect) HTTP status code.
Unlike RedirectResult
(302 Found) keeps Request's HTTP verb.
public class TemporaryRedirectResult : ActionResult
{
public string Url { get; }
public TemporaryRedirectResult(string url);
}
Example:
using System.Web.Mvc;
using System.Net;
using AspNet.Mvc.Common.ActionResults;
class HomeController : Controller
{
public ActionResult Index()
{
return new HttpCustomErrorResult(
HttpStatusCode.Conflict, "ApplicationSpecificErrorCode");
// ▶ Response Headers
// HTTP/1.1 409 Conflict
// ▶ Response
// ApplicationSpecificErrorCode
}
public ActionResult Home()
{
return new TemporaryRedirectResult(Url.Action("Index"));
// ▶ Response Headers
// HTTP/1.1 307 Temporary Redirect
// Location: /Home/Index
}
}
Global exception logger for AspNet MVC.
public class LoggingErrorAttribute : HandleErrorAttribute
{
public LoggingErrorAttribute(NLog.ILogger logger);
}
Example:
using AspNet.Mvc.Common.Logging;
class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new LoggingErrorAttribute(NLog.LogManager.GetLogger("*")));
}
}
Utility for detecting user's TimeZone.
static MvcHtmlString GenerateCookieScrpt()
Inject script tag for populating TimeZone cookie to Razor view.
static TimeSpan GetClientTimeZoneOffset(ActionExecutingContext filterContext)
Get TimeZone offset from cookie.
View:
@using AspNet.Mvc.Common.Helpers
@TimeZoneHelper.GenerateCookieScrpt()
Controller:
using AspNet.Mvc.Common.Helpers;
abstract class ControllerBase : Controller
{
protected internal TimeSpan TimeZoneOffset { get; private set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
TimeZoneOffset = TimeZoneHelper.GetClientTimeZoneOffset(filterContext);
}
}
Route that extracts "auth_token" parameter from query string like
http://host:port/path?name=value&auth_token=abcdef1234567890
and pass "auth_token" and entire URL to action:
RedirectResult RedirectController.Redirect(string url, string authToken);
Custom constraint for AspNet.Mvc Attribute Routing that maps string values in URL to specified enum.
[Route("/{enumValue:enum(MyNamespace.MyEnum)}")]
using AspNet.Mvc.Common.Routing;
static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.Add(new RedirectRoute());
var constraintsResolver = new DefaultInlineConstraintResolver();
constraintsResolver.ConstraintMap.Add("enum", typeof(EnumConstraint));
routes.MapMvcAttributeRoutes(constraintsResolver);
}
}
enum MyEnum { First, Second }
class RedirectController : Controller
{
[AllowAnonymous]
public ActionResult Redirect(string url, string authToken)
{
// verify authToken
if (authToken == null)
{
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
}
return new TemporaryRedirectResult(url);
}
[Route("/{enumValue:enum(MyEnum)}")]
public ActionResult GetEnum(MyEnum enumValue)
{
throw new NotImplementedException();
}
}
IHttpActionResult
for passing streams as files to client (streams are not materialized to RAM).
public class FileStreamResult : IHttpActionResult
{
public string FileName { get; }
public string ContentType { get; }
public FileStreamResult(Stream stream, string fileName, string contentType = null);
public FileStreamResult(string filePath, string contentType = null);
}
Example:
class FileController : ApiController
{
public IHttpActionResult DownloadFile(string filePath)
{
return new FileStreamResult(filePath, "application/json");
}
public IHttpActionResult DownloadMemoryStream()
{
// stream will be disposed later by AspNet Web API
var ms = new MemoryStream();
using (StreamWriter writer = new StreamWriter(ms, Encoding.UTF8))
{
writer.Write("test test test...");
}
ms.Seek(0, SeekOrigin.Begin);
return new FileStreamResult(ms, "MemoryStream.txt", "text/plain");
}
}
NLog global exception logger for AspNet Web API.
public class ApiExceptionLogger : ExceptionLogger
{
public ApiExceptionLogger(NLog.ILogger logger);
}
using System.Data.SqlClient;
using Common.Api;
using Common.Exceptions;
using Common.MethodMiddleware;
using static Common.Api.ApiHelper;
class Model { }
enum ErrorCodes { GeneralError }
class WebService
{
readonly MethodDecorator _methodDecorator;
readonly ApplicationService _applicationService;
public WebService(ApplicationService applicationService)
{
_applicationService = applicationService;
_methodDecorator = new MethodDecorator()
.Use(new WrapExceptionMiddleware());
}
public ApiResult<Model, ErrorCodes> DoSomething(Model argument)
{
return _methodDecorator.Execute(new { argument }, () =>
{
return _applicationService.DoSomething(argument);
});
}
public ApiResult<Model, ErrorCodes> DoSomethingElse(Model argument)
{
if (argument == null)
{
return Error(ErrorCodes.GeneralError, $"Argument {nameof(argument)} is required");
}
return Ok(new Model());
}
}
class ApplicationService
{
public Model DoSomething(Model argument)
{
if (argument == null)
{
throw new ValidationException(
nameof(argument), "Required", $"Argument {nameof(argument)} is required");
}
try
{
// do something
return argument;
}
catch (SqlException)
{
throw new BusinessException<ErrorCodes>(
ErrorCodes.GeneralError, "Something went wrong, please try again");
}
}
}
Structure for passing result of service operation with possible validation and logic errors.
public class ApiResult<TResult>
{
public bool IsSuccess { get; set; }
public virtual TResult Data { get; set; }
public string ErrorCode { get; set; }
public string ErrorMessage { get; set; }
public ValidationError[] ValidationErrors { get; set; }
}
public class ApiResult<TResult, TError>
where TError : struct
{
public bool IsSuccess { get; set; }
public virtual TResult Data { get; set; }
public TError? ErrorCode { get; set; }
public string ErrorMessage { get; set; }
public ValidationError[] ValidationErrors { get; set; }
}
Structure for passing status of service operation with possible validation and logic errors.
public class ApiStatus : IApiStatus, IApiError
{
public bool IsSuccess { get; set; }
public string ErrorCode { get; set; }
public string ErrorMessage { get; set; }
public ValidationError[] ValidationErrors { get; set; }
}
public class ApiStatus<TError> : IApiStatus, IApiError<TError>
where TError : struct
{
public bool IsSuccess { get; set; }
public TError? ErrorCode { get; set; }
public string ErrorMessage { get; set; }
public ValidationError[] ValidationErrors { get; set; };
}
Static helper for wrapping operation results and errors to common structures.
Ok()
Utility for returning result from method
Ok<TResult>(TResult data)
Utility for returning result from method
Error<TError>(TError code, string message = null)
Utility for returning error from method
Exception with error code and message that passed to end user of application.
public class BusinessException : Exception
{
public string Code { get; set; }
public BusinessException(string code, string message);
}
public class BusinessException<TError> : Exception
where TError : struct
{
public TError Code { get; set; }
public BusinessException(TError code, string message);
}
Exception for passing validation errors.
public class ValidationException : Exception
{
public ValidationError[] Errors { get; }
public ValidationException(string path, string code, string message);
public ValidationException(params ValidationError[] errors);
}
Some helpers for IDbConnection
and DbConnection
.
async Task<IDisposable> EnsureOpenAsync(this DbConnection connection)
If connection is already open before using()
statement then it stays open after using()
statement.
If connection is closed before using()
statement then it will be opened inside using()
and closed after using()
.
async Task<Stream> QueryBlobAsStreamAsync(this DbConnection connection, string sql, params object[] parameters)
Read SQL Blob value as stream. Sql Query should return one row with one column.
using Common.Extensions;
class SqlRepository
{
readonly DbConnection _connection;
public async Task ExecuteSomeQueryAsync()
{
using (await _connection.EnsureOpenAsync())
{
// execute some SQL
}
}
public async Task<Stream> ReadFileAsync(int fileId)
{
Stream stream = await _connection.QueryBlobAsStreamAsync(
"SELECT Content FROM Files WHERE Id = @fileId",
new SqlParameter("@fileId", fileId));
return stream;
}
}
Extensions for updating ICollection
of some domain entities from IEnumerable
of the relevant DTOs
List<Entity> entities;
Model[] models;
entities.MapFrom(entities, models)
.WithKeys(e => e.Id, m => m.Id)
.MapElements((e, m) =>
{
e.Property = m.Property;
});
Detailed example:
using System.Collections.Generic;
using System.Linq;
class OrderModel
{
public int Id { get; set; }
public ProductModel[] Products { get; set; }
}
class ProductModel
{
public int Id { get; set; }
public string Title { get; set; }
}
class OrderEntity
{
public int Id { get; set; }
public ICollection<ProductEntity> Products { get; } = new HashSet<ProductEntity>();
}
class ProductEntity
{
public int Id { get; set; }
public string Title { get; set; }
}
class OrderService
{
readonly DbContext _dbContext;
readonly ProductService _productService
public static void UpdateOrder(OrderEntity entity, OrderModel model)
{
entity.Id = model.Id;
entity.Products.MapFrom(model.Products)
.WithKeys(e => e.Id, m => m.Id)
.OnRemove(_dbContext.Products.Remove)
.MapElements(_productService.UpdateProduct);
}
}
class ProductService
{
public void UpdateProduct(ProductEntity entity, ProductModel model)
{
entity.Id = model.Id;
entity.Title = model.Title;
}
}
T[] Add<T>(this T[] array, T item)
T[] Remove<T>(this T[] array, T item)
T[] Replace<T>(this T[] array, T oldItem, T newItem)
bool SequenceEqual(this byte[] first, byte[] second)
byte[] ExtractBytes(this byte[] source, int offset, int count)
byte[] Concat(this byte[] first, byte[] second)
static byte[] Combine(params byte[][] arrays)
Concat multiple ByteArrays.
byte[] HmacSign(this byte[] rawMessage, byte[] hmacKey)
Sign message with HMAC algorithm. Throws CryptographicException
.
byte[] HmacExtract(this byte[] hmacMessage, byte[] hmacKey)
Extract HMAC signed message.
void ForEach<T>(this IEnumerable<T> source, Action<T> action)
Like List<T>.ForEach(Action<T> action)
.
HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
Create HashSet<T>
from IEnumerable<T>
.
IEnumerable<TItem> DistinctBy<TItem, TKey>(this IEnumerable<TItem> source, Func<TItem, TKey> keySelector)
Like Distinct()
but uses values from keySelector
for equality check.
IEnumerable<T> OmitRepeated<T>(this IEnumerable<T> source)
Remove repeated values from sequence.
IEnumerable<TItem> OmitRepeatedBy<TItem, TKey>(this IEnumerable<TItem> source, Func<TItem, TKey> keySelector)
Like OmitRepeated()
but uses values from keySelector
for equality check.
bool In<TEnum>(this TEnum value, params TEnum[] values)
color.In(Colors.First, Colors.Second)
is equivalent to color == Colors.First || color == Colors.Second
.
Dictionary<TEnum, bool> ToDictionary<TEnum>(this TEnum value)
Convert [Flags] enum
to Dictionary<TEnum, bool>
.
Dictionary<TEnum, bool> ToDictionary<TEnum>(this TEnum? value)
Convert nullable [Flags] enum
to Dictionary<TEnum, bool>
.
TEnum? ToEnum<TEnum>(this IDictionary<TEnum, bool> value)
Convert Dictionary<TEnum, bool>
to nullable [Flags] enum
.
string TrimWhiteSpace(this string input)
Replace all long white space inside string by one space character.
bool IsBase64(this string value)
Check if string is Base64 string.
T AsSyncronous<T>(this Task<T> task)
Execute Task
synchronously.
void AsSyncronous(this Task task)
Execute Task
synchronously.
Simple DSL based on C# 6 String Interpolation for building dynamic SQL queries.
using static Common.Helpers.StringInterpolationHelper;
class EmployeesFilter
{
public bool IncludeDepartment { get; set; }
public int? DepartmentId { get; set; }
public string[] Names { get; set; }
}
class EmployeesSearchService
{
public void SearchEmployees(EmployeesFilter filter)
{
string sql = $@"
SELECT
{@if(filter.IncludeDepartment, @"
dep.Id,
dep.Name,"
)}
emp.Id,
emp.Name,
emp.DepartmentId
FROM Emloyees AS emp
{@if(filter.IncludeDepartment, @"
LEFT JOIN Departments AS dep ON dep.Id = emp.DepartmentId"
)}
WHERE
{@if(filter.DepartmentId != null, @"
emp.DepartmentId = @DepartmentId",
@else(@"
emp.DepartmentId IS NULL"
))}
AND (
{@foreach(filter.Names, name =>
$"emp.Name LIKE '{name}%'",
" OR "
)}
)";
}
}
class ProductsFilter
{
public string Title { get; set; }
public decimal? MinPrice { get; set; }
public decimal? MaxPrice { get; set; }
public string[] Tags { get; set; }
public int? TagsLength => Tags?.Length;
public ProductsSortBy SortBy { get; set; }
}
enum ProductsSortBy { Title, Price }
class ProductsSearchService
{
public void SearchProducts(ProductsFilter filter)
{
string sql = $@"
SELECT
p.Title,
p.Description,
p.Price
FROM Products AS p
WHERE 1 = 1
{@if(filter.Title != null, @"
AND p.Title = @Title"
)}
{@if(filter.MinPrice != null, @"
AND p.Price >= @MinPrice"
)}
{@if(filter.MaxPrice != null, @"
AND p.Price <= @MaxPrice"
)}
{@if(filter.Tags != null, $@"
AND (
SELECT COUNT(1)
FROM ProductTags AS t
WHERE t.ProductId = p.Id
AND t.Tag IN (@Tags)
) = @TagsLength"
)}
ORDER BY
{@switch(filter.SortBy,
@case(ProductsSortBy.Title, " Title ASC"),
@case(ProductsSortBy.Price, " Price ASC")
)}";
}
}
Utils for Full Text Search in Microsoft SQL Server
string PrepareFullTextQuery(string searchPhrase, bool fuzzy = false, int minWordLength = 3)
Build query for SQL Server FTS Engine CONTAINS function.
Result should be passed through ADO.NET SqlParameter due to preventing SQL Injection.
using System.Diagnostics;
using Common.Helpers;
class SqlServerFullTextSearchService
{
public void SearchArticles(string title = "Я на Cолнышке лежу")
{
string ftsQuery = SqlFullTextSearchHepler.PrepareFullTextQuery(title, fuzzy: true);
// "cолнышке*" NEAR "лежу*" OR FORMSOF(FREETEXT, "cолнышке") AND FORMSOF(FREETEXT, "лежу")
string sql = @"
SELECT TOP (10)
a.Id,
a.Title,
a.Content,
fts.[RANK]
FROM CONTAINSTABLE(Departments, (Title), @ftsQuery) AS fts
INNER JOIN Articles AS a ON fts.[KEY] = a.ID
ORDER BY fts.[RANK] DESC";
}
}
ulong MurmurHash3(ulong key)
Compute MurMurHash
uint ReverseBits(uint value)
Reverse bits in [Flags] enum
value for use in OrderBy()
extension
using System;
using System.Collections.Generic;
using System.Linq;
using Common.Helpers;
[Flags]
enum UserRoles
{
Admin = 1, Moderator = 2, User = 4, Reader = 8,
}
class User
{
public UserRoles Roles { get; set; }
}
static class UserExtensions
{
public static IEnumerable<User> OrderByRoles(this IEnumerable<User> users)
{
return users.OrderByDescending(u => BitHelper.ReverseBits((uint)u.Roles));
}
}
void CleanDirectory(string path)
Reqursively delete all files and folders from directory.
string RemoveInvalidCharsFromFileName(string fileName)
Cleanup fileName
from invalid characters.
string GetHost(string uriString)
"http://localhost/SomeApp" => "localhost"
string AddTrailingSlash(string url)
"http://localhost/SomeApp" => "http://localhost/SomeApp/"
string ChangeHost(string absoluteUrl, string host)
("http://localhost:8080/SomeApp", "127.0.0.1") => "http://127.0.0.1:8080/SomeApp"
bool CanonicalEqual(string url1, string url2)
"http://localhost/SomeApp" == "http://localhost/someapp/"
Utility for performing transliteration.
static string TransliterateIcao(char input)
Transliterate input
symbol. Based on ICAO standard.
static string TransliterateIcao(char input)
Transliterate input
string symbol by symbol. Based on ICAO standard.
Utility that skips simultaneous execution of async tasks with same type.
async Task ExecuteAsync(Func<Task> asyncAction)
async Task StopAsync()
using Common.Jobs;
class JobsService
{
readonly AsyncJobsManager _asyncJobsManager;
private async Task FirstJob()
{
await Task.Delay(200);
}
private async Task SecondJob()
{
await Task.Delay(300);
}
public async Task RunJobs()
{
for (int i = 0; i < 5; i++)
{
_asyncJobsManager.ExecuteAsync(FirstJob);
_asyncJobsManager.ExecuteAsync(SecondJob);
}
await Task.Delay(500);
// FirstJob will be executed three times (600ms)
// SecondJob will be executed two times (600ms)
await _asyncJobsManager.StopAsync();
}
}
Utility for de(serialiaing) MailMessage
to byte array. Supports .NET 4.0, 4.5.
static byte[] Serialize(MailMessage msg)
static MailMessage Deserialize(byte[] binary)
static MailMessage ReadMailMessage(this BinaryReader r)
static void Write(this BinaryWriter w, MailMessage msg)
using Common.Mail;
class DelayedMailSender
{
public void StoreMessage(string filePath)
{
MailMessage msg = new MailMessage(
new MailAddress("test1@mail.com", "Address1"),
new MailAddress("test2@mail.com", "Address2"))
{
Subject = "subject sucbejct",
Body = "Message Body",
IsBodyHtml = false,
Priority = MailPriority.High,
};
msg.CC.Add(new MailAddress("test3@mail.com", "Address3"));
msg.Bcc.Add(new MailAddress("test4@mail.com"));
msg.ReplyToList.Add("test5@mail.com");
byte[] serializedMsg = MailMessageBinarySerializer.Serialize(msg);
File.WriteAllBytes(filePath, serializedMsg);
}
public void SendStoredMessage(string filePath)
{
byte[] serializedMsg = File.ReadAllBytes(filePath);
MailMessage msg = MailMessageBinarySerializer.Deserialize(serializedMsg);
using (var client = new SmtpClient("mysmtphost")
{
DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory,
PickupDirectoryLocation = Path.GetTempPath(),
})
{
client.Send(msg);
}
}
}
public class SmtpConnectionSettings
{
public string Server { get; set; }
public int Port { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public bool EnableSsl { get; set; }
}
Utility for checking availability of SMTP Server.
public class SmtpConnectionChecker
{
public SmtpConnectionChecker(ISmtpConnectionSettings settings);
public virtual bool ServerIsReady();
}
### SmtpMailSender
Utility for sending mail messages and handling errors.
```cs
public class SmtpMailSender
{
public SmtpMailSender(SmtpConnectionSettings settings, NLog.ILogger logger);
public virtual async Task<bool> TrySend(MailMessage message);
public virtual async Task<bool> TrySend(byte[] serializedMessage);
}
Example:
using System.Diagnostics;
using System.Net.Mail;
using System.Threading.Tasks;
using Common.Smtp;
class MailService
{
readonly SmtpConnectionChecker _checker;
readonly SmtpMailSender _sender;
public async Task SendMailMessage(MailMessage message)
{
while (!_checker.ServerIsReady())
{
await Task.Delay(1000);
}
bool success = await _sender.TrySend(message);
Debug.WriteIf(!success, "MailMessage sending failed");
}
}
Like Lazy<T>
but for wrapping async values.
public class AsyncLazy<T> : Lazy<Task<T>>
{
public AsyncLazy(Func<T> valueFactory);
public AsyncLazy(Func<Task<T>> taskFactory);
}
Example:
using Common.Utils;
class AsyncService
{
readonly AsyncLazy<string> LazyString = new AsyncLazy<string>(async () =>
{
await Task.Delay(1000);
return "lazy string";
});
async Task Method()
{
string str = await LazyString;
// do somethig with this str
}
}
Wrapper for Stream
that dispose boundObject
when stream is disposed.
public class DisposableStream : Stream
{
public DisposableStream(IDisposable boundObject, Stream wrappedStream);
}
Example:
class BlobStreamingService
{
readonly DbConnection _connection;
async Task<Stream> StreamFile(int fileId)
{
// error handling is skipped
await _connection.OpenAsync();
DbCommand command = _connection.CreateCommand();
command.CommandText = "SELECT TOP (1) Content FROM Files WHERE Id = @fileId";
command.Parameters.Add(new SqlParameter("@fileId", fileId));
DbDataReader reader = await command.ExecuteReaderAsync();
await reader.ReadAsync();
// reader will be disposed with wrapped stream
return new DisposableStream(reader, reader.GetStream(0));
}
}
Random class replacement with same API but with usage of RNGCryptoServiceProvider inside.
public class CryptoRandom : Random { }
EntityKeyMember[] GetPrimaryKeys(this DbContext context, object entity)
Get composite primary key from entity.
IEnumerable<DbEntityEntry> GetChangedEntries(this DbContext context, EntityState state)
Get changed entities from change tracker.
void Touch(this DbContext context)
Check if DbContext.Database
connection is alive.
Task TouchAsync(this DbContext context)
Check if DbContext.Database
connection is alive.
IDbTransaction BeginTransaction(this DbContext context, IsolationLevel isolationLevel)
Create transaction with IDbTransaction
interface (instead of DbContextTransaction
).
void ExecuteInTransaction(this DbContext context, Action action)
Execute Action
in existing transaction or create and use new transaction.
T ExecuteInTransaction<T>(this DbContext context, Func<T> method)
Execute Func<T>
in existing transaction or create and use new transaction.
Task ExecuteInTransaction(this DbContext context, Func<Task> asyncAction)
Execute Func<Task>
in existing transaction or create and use new transaction.
Task<T> ExecuteInTransaction<T>(this DbContext context, Func<Task<T>> asyncMethod)
Execute Func<Task<T>>
in existing transaction or create and use new transaction.
TableAndSchema GetTableAndSchemaName(this DbContext context, Type entityType)
Get corresponding table name and schema by entityType
.
TableAndSchema[] GetTableAndSchemaNames(this DbContext context, Type entityType)
Get corresponding table name and schema by entityType
.
Use it if entity is splitted between multiple tables.
using EntityFramework.Common.Extensions;
class Passport
{
[Key, Column(Order = 1)]
public string Series { get; set; }
[Key, Column(Order = 2)]
public string Code { get; set; }
}
class PassportsContext : DbContext
{
public DbSet<Passport> Passports { get; }
}
class PassportsService
{
readonly PassportsContext _context;
void Method()
{
var passport = new Passport { Series = "123456", Code = "7890" };
_context.Passports.Add(passport);
var keys = _context.GetPrimaryKeys(passport);
// keys == [{ Key = "Series", Value = "123456" }, { Key = "7890", Value = "7890" }]
var addedEntities = _context.GetChangedEntries(EntityState.Added);
// addedEntities[0].Entry == passport;
var tableAndSchema = _context.GetTableAndSchemaName(typeof(Passport));
// tableAndSchema.TableName == "Passports"; tableAndSchema.Schema == "dbo"
// uses existing transaction, otherwise creates new one
_context.ExecuteInTransaction(() =>
{
_context.SaveChanges();
_context.SaveChanges();
});
using (IDbTransaction transaction = _context.BeginTransaction())
{
_context.SaveChanges();
transaction.Commit();
}
}
}
async Task<List<TResult>> ExecuteChunkedInQueryAsync<TResult, TParameter>(
this IQueryable<TResult> baseQuery,
Expression<Func<TResult, TParameter>> propertyGetter,
IEnumerable<TParameter> inValues,
int chunkSize = 500)
Converts a query with big IN clause to multiple queries with smaller IN clausesand combines the results.
using EntityFramework.Common.Extensions;
class Post
{
public int Id { get; set; }
public int AuthorId { get; set; }
public DateTime Date { get; set; }
}
class BlogContext : DbContext
{
public DbSet<Post> Posts { get; set; }
}
class PostsService
{
readonly BlogContext _context;
public async Task<List<Post>> GetPostsAsync(DateTime sinceDate, IEnumerable<int> authorIds)
{
return await _context.Posts
.Where(p => p.Date >= sinceDate)
.ExecuteChunkedInQueryAsync(p => p.AuthorId, authorIds, chunkSize: 100);
}
}
Structure that represents table name and schema.
struct TableAndSchema
{
public string TableName;
public string Schema;
}
var (table, schema) = new TableAndSchema();
IDbCommandInterceptor
implementation for logging errors from SQL-queries.
class NLogDbInterceptor : IDbCommandInterceptor
{
public NLogDbInterceptor(NLog.ILogger logger);
}
A wrapper that allows to present EF transactions (DbContextTransaction
) as IDbTransaction
.
For example, if you need to implement an interface that requires you to return IDbTransaction
.
Used by DbContextExtensions.BeginTransaction()
.
class DbTransactionAdapter : IDbTransaction
{
public DbTransactionAdapter(DbContextTransaction transaction);
}
Custom value converter for passing string properties as RAW JSON values.
using Newtonsoft.Json.Common.Converters;
class Book
{
[JsonConverter(typeof(RawJsonConverter))]
public string Chapters { get; set; }
}
class BookService
{
public string GetBookJson()
{
var book = new Book
{
Chapters = "[1, 2, 3, 4, 5]",
};
return JsonConvert.SerializeObject(book);
// {"Chapters": [1, 2, 3, 4, 5]}
// instead of
// {"Chapters": "[1, 2, 3, 4, 5]"}
}
}
Utility for creating System.Net.Mail.MailMessage
from Razor view.
public class MailTemplateEngine
{
public MailMessage CreateMessage(
string from,
string to,
string templatePath,
object model,
string fromName = null,
bool isBodyHtml = false);
public Attachment CreateAttachment(
string templatePath,
object model,
string mediaType = "text/plain");
}
Example:
using System.Net.Mail;
using RazorEngine.Common.Mail;
class EmailModel
{
public string UserName { get; set; }
public string PasswordLink { get; set; }
}
class EmailService
{
readonly MailTemplateEngine _mailTemplateEngine;
public MailMessage CreateEmail(EmailModel model)
{
MailMessage message = _mailTemplateEngine.CreateMessage(
from: "site@example.com",
to: "user@example.com",
templatePath: "~/Views/Email/ResetPassword.cshtml",
model: model,
fromName: "My awesome site",
isBodyHtml: true);
Attachment attachment = _mailTemplateEngine.CreateAttachment(
templatePath: "~/Views/Email/Attachments/ResetPassword.cshtml",
model: model);
attachment.ContentId = "password-links";
message.Attachments.Add(attachment);
return message;
}
}
~/Views/Email/ResetPassword.cshtml
@model EmailModel
@{
ViewBag.Subject = "Please reset your password";
}
Dear @Model.UserName, please <a href="@Model.PasswordLink">reset</a> your password.
~/Views/Email/Attachments/ResetPassword.cshtml
@model EmailModel
@{
ViewBag.FileName = "reset_password_link_" + Model.UserName + ".txt";
}
@Model.PasswordLink