Ana içeriğe geç
Version: 1.0.1

Core

It is the library where Milvasoft libraries share common components. In this part of the document, we will describe the components found in the core library one by one.

Entity Bases

Entities are one of the core concepts of DDD (Domain Driven Design). Eric Evans describes it as "An object that is not fundamentally defined by its attributes, but rather by a thread of continuity and identity". An entity is generally mapped to a table in a relational database.

All methods and entities in database related libraries and components are inherited from this class.

You can use BaseEntity to use Id which inherited from IEquatable interface and whose type is struct

public abstract class BaseEntity<TKey> : IBaseEntity<TKey> where TKey : struct, IEquatable<TKey>
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual TKey Id { get; set; }

public override string ToString() => $"[{GetType().Name} {Id}]";
}

If you want to use a different type of Id, you can use EntityBase

public abstract class EntityBase<TKey> : IEntityBase<TKey>
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual TKey Id { get; set; }

public override string ToString() => $"[{GetType().Name} {Id}]";
}

If you do not want to derive your entity from the base BaseEntity<TKey> or EntityBase<TKey> class, you can directly implement IBaseEntity<TKey> or IEntityBase<TKey> interface.

Base Classes & Interfaces for Audit Properties

There are some properties like CreationDate, CreatorUserId, LastModificationDate and much more, which are very common in all applications. Milvasoft provides some interfaces and base classes to standardize these properties and also sets their values automatically.

There are a lot of auditing interfaces, so you can implement the one that you need. While you can manually implement these interfaces, you can use the base classes defined in the next section to simplify it. You can see all these interfaces here.

IHasCreator defines the following properties;

  • CreatorUserId

ICreationAuditable has multiple versions and inherits from the IHasCreator, so it defines the following properties;

  • CreationDate

  • CreatorUserId

  • CreatorUser

IHasModifier defines the following properties;

  • LastModifierUserId

IAuditable has multiple versions and inherits from the IHasModifier and ICreationAuditable, so it defines the following properties;

  • LastModificationDate
  • LastModifierUserId
  • LastModifierUser
  • CreationDate
  • CreatorUserId
  • CreatorUser

ISoftDeletable defines the following properties;

  • IsDeleted
  • DeletionDate

IHasDeleter defines the following properties;

  • DeleterUserId

IFullAuditable has multiple versions and inherits from the ISoftDeletable,IHasDeleter,ICreationAuditable and IAuditable, so it defines the following properties;

  • IsDeleted
  • DeletionDate
  • DeleterUserId
  • DeleterUser
  • LastModificationDate
  • LastModifierUserId
  • LastModifierUser
  • CreationDate
  • CreatorUserId
  • CreatorUser

Auditing Base Classes

While you can manually implement any of the interfaces defined above, it is suggested to inherit from the base classes defined here;

CreationAuditableEntity implement the ICreationAuditable interface. AuditableEntity implement the IAuditable interface. FullAuditableEntity implement the IFullAuditable interface.

All these base classes also have multiple versions. You can check here thesee versions.

All these base classes also have ...WithUser pairs, like FullAuditableWithUser and FullAuditableWithCustomUser. This makes possible to add a navigation property to your user entity. It is also available in a version that you can use with the Microsoft.AspNetCore.Identity library.

Referenced source document

Models

It contains models that you can use in the library or in your projects.

Response Models

BaseResponse

Base response model for API responses.

PropertiesDescriptionDefault Value
SuccessInformation of whether the request was successful.true
MessageMessage of response."Process Succesful!"
StatusCodeHttp status code of response.MilvaStatusCodes.Status200OK

ExceptionResponse

Response model that can be returned in error situations.

PropertiesDescription
ResultResult of request.
ErrorCodesError codes of request, if exists.

ObjectResponse

Response model for requests that return a object.

PropertiesDescription
ResultResult object.

File Models

IFileDTO

Abstraction for file upload process.

PropertiesDescription
FileNameName of file.
FileFile object.

IFileEntity

Abstraction for file upload process.

PropertiesDescription
FileNameName of file.
FilePathPath of file.

InvalidString

The model used to control the blacklist with dependency injection of the attributes that perform string validation.

PropertiesDescription
NameName of invalid string.
ValuesInvalid values.

OrderByProp

Model used to forward multiple sort requests from the client to the database.

PropertiesDescription
PropNameDetermines order by Property name of entity.
AscendingDetermines order by ascending or descending.
PriorityPriority of order operation. Ex. first order by creation date then order by updated date.

PaginationDTO

Model for use in retrieving paging parameters from the client.

PropertiesDescription
DTOListItems in page.
PageCountPage count information.
TotalDataCountTotal data count of all pages.

Exceptions

With the server-side checks, you can throw custom exceptions and catch them with a custom middleware. In this way, you can both check for errors without breaking your application and forward your user-friendly messages to the user by rewriting the response. For exception types and overloads check here.

All exception classes in below inherit from the MilvaBaseException class.

Example middleware for usage example;


public class ExceptionHandlerMiddleware
{
private readonly RequestDelegate _next;

public ExceptionHandlerMiddleware(RequestDelegate next)
{
_next = next;
}

public async Task Invoke(HttpContext context)
{
// Get localizer instance from service collection
var sharedLocalizer = context.RequestServices.GetLocalizerInstanceWithMilvaResource();

string message = sharedLocalizer[nameof(ResourceKey.MiddlewareGeneralErrorMessage)];

List<int> errorCodes = new();

try
{
context.Request.EnableBuffering();

//Continue processing
if (_next != null)
await _next.Invoke(context);
}
catch (Exception ex)
{
int errorCode = MilvaStatusCodes.Status600Exception;

if (ex is MilvaUserFriendlyException userFriendlyEx)
{
if (userFriendlyEx.ExceptionObject == null)
message = sharedLocalizer[userFriendlyEx.Message];
else
message = sharedLocalizer[userFriendlyEx.Message, userFriendlyEx.ExceptionObject];

errorCodes.Add(userFriendlyEx.ExceptionCode);
}
else
{
if (ex is MilvaDeveloperException devEx)
{
// Send mail this error to developers
}
else
{
message = sharedLocalizer[nameof(ResourceKey.AnErrorOccured)];

// Get logger instance from service collection
var logger = context.RequestServices.GetRequiredService<IMilvaLogger>();

// Log exception
logger.Write(SeriLogEventLevel.Fatal, ex, ex.Message);
}

}

// Rewrite response
if (!context.Response.HasStarted)
{
var response = new ExceptionResponse
{
Message = message,
StatusCode = errorCode,
Success = false,
Result = new object(),
ErrorCodes = errorCodes
};

// Serialize with Newtonsoft.Json library
var json = JsonConvert.SerializeObject(response);

context.Response.ContentType = MimeTypeNames.ApplicationJson;

// We prefer to set the HTTP status codes of all responses to 200.
context.Response.StatusCode = MilvaStatusCodes.Status200OK;

await context.Response.WriteAsync(json);
}
}
}
}

MilvaDeveloperException

It can be used to distinguish developer-related errors.

MilvaUserFriendlyException

It can be used to distinguish errors made by the users your application serves.

Usage


...

List<Product> products = await _productRepository.GetProductsFromDatabaseAsync();

if(products.IsNullOrEmpty())
throw new MilvaUserFriendlyException("CannotFindProductsLocalizerKey");

...

The above middleware catches the error and rewrites the response.

Response;

{
"result": {},
"errorCodes": [
600
],
"success": false,
"message": "No products found. Please add new products!",
"statusCode": 600
}

MilvaValidationException

Error class that can be thrown on validation errors.

MilvaTestException

An error class that we usually use when testing the application, the text of which we can throw in any situation we specify ourselves.


Extensions

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. There are many extension methods in the library. We will explain a few of them, for more you can check here.

Expression Extensions

Append()

Combines two expressions.

 public static Expression<Func<T, bool>> Append<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right, ExpressionType expressionAppendType)

Usage


Expression<Func<Product, bool>> priceCondition = i => i.Price > 10;
Expression<Func<Product, bool>> isDeletedCondition = i => i.IsDeleted;

// Output : i => i.Price > 10 && i.IsDeleted
var combinedCondition = priceCondition.Append(isDeletedCondition , ExpressionType.AndAlso);

Generic Collection Extensions

IsNullOrEmpty()

Checks whether or not collection is null or empty. Assumes collection can be safely enumerated multiple times.

Usage


var list = new List<string>();

// Output : true
var isNullOrEmpty = list.IsNullOrEmpty();

String Extensions

ToUpperFirst()

Uppercases the first letter of the word.

Usage


var value = "test";

// Output : Test
var upperValue = value.ToUpperFirst();

GeoLocation

Helper for geographic calculations.

Models

GeoPoint

PropertiesDescription
LatitudeLatitude of point.
LongitudeLongitude of point.

Extensions

Extension methods for geo calculation.

CalculateDistance

Calculates the distance in km from point1 to point2.

public static double CalculateDistance(this GeoPoint point1, GeoPoint point2)

Usage

var firstPoint = new GeoPoint(11, 11);

var secondPoint = new GeoPoint(12, 12);

// Output = 155.68179481457878
var distance = firstPoint.CalculateDistance(secondPoint);

You can confirm this values from here.

Common Components

Request Sender

What is Http Request ?

An HTTP request is made by a client, to a named host, which is located on a server. The aim of the request is to access a resource on the server. To make the request, the client uses components of a URL (Uniform Resource Locator), which includes the information needed to access the resource. The components of a URL explains URLs. For more information.

Url schema;


https://www.milvasoft.com:444/api/v1.0/products/product?id=1
_____ ________________ ___ _______________________ ____
| | | | |
protocol | | pathName query
hostName |
port

This class simplifies http request sending.

Usage


// Creates proper request url
var url = Request.CreateRequestUrlFromAddress("https://www.milvasoft.com", "api/v1.0/products/product");

var headers = new KeyValuePair<string, string>[]
{
new KeyValuePair<string, string>("Accept-Language", "en-US")
};

// Request Body
var product = new Product
{
Id = 1,
Name = "Water"
};

// Creates HttpRequestMessage object according to request parameters
using var requestMessage Request.CreateRequestMessage(HttpMethod.Post, Encoding.UTF8, "json", url, product, headers);

// Don't use HttpClient like this in your projects. Use instead AddHttpClient() service collection extension.
using var httpClient = new HttpClient();

// Sends the request and deserializes the response to the requested type.
var response = await httpClient.SendRequestDeserialize<ObjectResponse>(requestMessage);

For more overloads check here.

Response Modifier

In cases where the api returns a response in a specific structure, you must create the response object at each endpoint. This class was created to reduce code duplication, increase readability, and standardize error messages to be returned. There are extension methods according to the response models in the Models section. You can check here and here to understand what the methods do.

Usage

Sample from Response.cs


[HttpPost("Login")]
public async Task<IActionResult> LoginAsync([FromBody] LoginDTO loginDTO)
{
var loginResult = await _accountService.LoginAsync(loginDTO);

return loginResult.GetObjectResponse(_sharedLocalizer["SuccessfullyLoginMessage"]);
}


{
"result": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
},
"success": true,
"message": "Successfully logged in.",
"statusCode": 200
}

Sample from ResponseV2.cs;


[HttpGet("Products")]
public async Task<IActionResult> GetProductsAsync()
{
var products = await _productService.GetProductsAsync();

return products.GetObjectResponseByEntities(HttpContext, "Product", isFiltering : false);
}


{
"result": [
{
"id": 121,
"name": "Water",
"description": "Water is life!"
},
{
"id": 122,
"name": "Tea",
"description": "Warm yourself!"
}
],
"success": true,
"message": "The operation was completed successfully.",
"statusCode": 200
}

Regex Matcher

What Is Regex ?

Regular expressions provide a powerful, flexible, and efficient method for processing text. The extensive pattern-matching notation of regular expressions enables you to quickly parse large amounts of text to:

  • Find specific character patterns.
  • Validate text to ensure that it matches a predefined pattern (such as an email address).
  • Extract, edit, replace, or delete text substrings.
  • Add extracted strings to a collection in order to generate a report.
    For many applications that deal with strings or that parse large blocks of text, regular expressions are an indispensable tool. For more information.

MatchRegex

Checks whether the entered value conforms to the requested regex or not.

Usage


var regex = "^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"

var value = "[email protected]";

// Output : true
var result = value.MatchRegex(regex);

For more helpers.

Common Helper Methods

This class contains helper methods. You can check here to understand what the methods do.

PropertyExists Methods

Checks whether the specified type has the named property.

Usage

bool propExists = CommonHelper.PropertyExists<TEntity>("propertyName");
Poco poco = new();

bool propExists = poco.PropertyExists("propertyName");
Type pocoType = new Poco().GetType();

bool propExists = pocoType.PropertyExists("propertyName");

CreateOrderByKeySelector Methods

Creates order by key selector expression according to property name. If property is not exists thw

Usage


// Output expression : i => i.Roles
Expression<Func<User, object>> selector = CommonHelper.CreateOrderByKeySelector<User>("Roles");


// Output expression : i => i.Username
Expression<Func<User, string>> selector = CommonHelper.CreateOrderByKeySelector<User, string>("Username");

EnsureLegalLengths

When checking the maximum and minimum length, it controls the maximum and minimum length values.

Usage


// Throws MilvaUserFriendlyException.
CommonHelper.EnsureLegalLengths(maxLength : 5, minLength : 10);

GetEnumDesciption

Returns the value of the DescriptionAttribute of the enum value.

Usage


public enum PocoType
{
[Description("This is a type!")]
SomeType,
}


var enumValue = PocoType.SomeType;

// Output : This is a type!
var description = enumValue.GetEnumDesciption();

ToGuid

Converts int value to Guid.

Usage


int intValue = 1;

// Output : "00000001-0000-0000-0000-000000000000"
var guidValue = intValue.ToGuid();

ToObjectId

Converts int value to ObjectId.

Usage


int intValue = 1;

// Output : "000000000000000000000001"
var objectIdValue = intValue.ToObjectId();

ToJson

Converts value to json.

Usage


var poco = new Product
{
Id = 1,
Name = "T-Shirt"
};

var value = poco.ToJson();

Output;


{
Id : 1,
Name : "T-Shirt"
}

ToObject

Converts json string value to specified T type.

CustomErrorResponse

Prepares custom validation model for response.

IsBetween Methods

Performs a "between comparision" for dates or times.

Usage

var date = new DateTime(2022, 5, 5, 5, 5, 5);

bool between = date.IsBetween(time1, time2, false);

Localizer Methods

Creates localizer instance if IStringLocalizerFactory registered to service collection.

Usage


...

var localizer = httpContext.RequestServices.GetRequiredLocalizerInstance<SharedResource>();

...

Networking

Provides access host machine's network statistics.

GetIPAddressesAsync

Gets all the IP addresses of the server machine hosting the application.

public static async Task<IPAddress[]> GetIPAddressesAsync()

GetIPAddressAsync

Returns the (first available or chosen) IP address of the server machine.

public static async Task<IPAddress> GetIPAddressAsync(int num = 0)

HasIPAddressAsync

Checks if the given IP address is one of the IP addresses registered to the server machine hosting the application.

public static async Task<bool> HasIPAddressAsync(IPAddress ipAddress)

This method returns TRUE if the IP address is registered, FALSE otherwise.

Other overload;

public static async Task<bool> HasIPAddressAsync(string ipAddress)

This method returns TRUE if the IP address is registered, FALSE otherwise.

This class have sync versions of this methods. For more information.

Filter Helpers

It contains helper methods for filtering operations. For more.