DOCUMENTATION

DOCUMENTATION

  • Official Web Site

›FAQ

Before We Start

  • What is Net Core Genesis
  • Prerequisites
  • Software Architecture
  • Future Vision

Getting Started

  • Release Notes
  • Direct Download as Boilerplate
  • Download AutoCode Solution Generator
  • Using AutoCode Solution Generator
  • Run Your Web Application
  • Tutorial

General

  • API Layer
  • Authentication & Authorization
  • Blockchain
  • Built-in UI Components
  • CLI Commands
  • Communication Middleware
  • Configuration
  • Data Access
  • Demo & Live Preview
  • Distributed Cache
  • Elastic Stack
  • Exception Handling
  • Folder Structures
  • Fundamentals
  • Genesis Database
  • Grid
  • JSON-Based UI Screen Rendering
  • Localization
  • Logging
  • Management Module
  • Microservice Architecture
  • Multi-Tenancy
  • Queuing & Message Broker
  • Adding New Page
  • Scheduler & Background Jobs
  • Workflow

FAQ

  • Security
  • Custom UI Pages
  • Backend Questions
  • UI Questions
  • Useful Documents

Server & System

  • Server Requirements
  • Server Setup & Deployment
  • Deploy on Web Server
  • DevOps & Dockerization
  • Performance & Scalability
  • CI/CD Management

Other

  • 3rd Party Licences
  • BaGet Server Installation

Backend Questions

Soft delete

By default, generated methods executes hard delete.

Hard delete method from a repository:

public bool Delete(SampleClassName entity)
{
    using (var context = GetDbContext<Your_DBContext>())
    {
        context.Set<SampleClassName>().Remove(entity);
        context.SaveChanges();
        return true;
    }
}

You can change it to a sample soft delete method from CoreCompanyRepository.cs which manages deletion by only status change:

public bool Delete(CoreCompany entity)
{
    using (var context = Helper.GetGenesisContext())
    {
        entity = context.Set<CoreCompany>().Find(entity.CompanyId);
        if (entity == null)
            return false;

        entity.Status = (int) Status.Deleted;
        context.Set<CoreCompany>().Update(entity);
        context.SaveChanges();
        return true;
    }
}

Or instead, elegantly you can manage it by overriding DBContextsEx's SaveChanges method.

Sending email and SMS

Instead of lots of coding, we recommend using communication middleware via Management / Communication

Resource code and Action for authorization

Please check all details in Authentication & Authorization

Logging

There exists a comprehensive logging middleware. Please check all details in Logging Middleware

Exclude a model/class property from logging

By adding [IgnoreLogging], [MaskedLogging] and [HashedLogging] attributes to model classes, you can manage logging for specific cases.

public class User
{
    [IgnoreLogging] // Do not log "password" ever
    [Column("password")]
    public string Password { get; set; }

    [MaskedLogging(@"\w\w(.*)\w")] // Log the value of "ibanNumber" as masked
    [Column("ibanNumber")]
    public string IbanNumber { get; set; }

    [HashedLogging] // Log the value of "email" as hashed
    [Column("email")]
    public string Email { get; set; }
}

Please check all details in Logging Middleware

Base classes

Through base classes you can manage main classes in a centralized manner.

  • Controllers implement BaseController
  • Models implement BaseContract
  • Repositories implement BaseRepository
  • DBContexts implement ContextBase
  • Validators implement AbstractValidator

Generic response object

There is a standard and generic reponse object returned for any method call.

PropertyType
successboolean
messagestring
errorsJSON Object Array
dataJSON Object, JSON Object Array

Example:

{   
    "success":  false,  
    "message":  "Something went wrong",
    "errors":  [  
                    {  
                        "errorCode":  "1020",           
                        "errorMessage":  "fullName cannot be null",  
                        "propertyName":  "fullName"
                    }
                ],
    "data":  {}  
}

EF Core performance

One of the most common pieces of advice is to use method called .AsNoTracking(). Supposedly this method greatly improves performance on EF queries.

Dapper sample

You can find a sample use in Admin/Admin.Data/Repositories/AuthRepository.cs

public LoggedInUser Login(LoggedInUser parameters)
{
    using (IDbConnection dbConnection = Connection)
    {
        dbConnection.Open();
        DynamicParameter p = new DynamicParameter();

        p.Add("p_email", parameters.Email);
        p.Add("p_password", CoreHelper.GetHashedString(parameters.Password));

        return dbConnection.Query<LoggedInUser>("adm_validateuser", p, commandType: CommandType.StoredProcedure).FirstOrDefault();
    }
}

Exception Handling

You don't need to handle exceptions in most of the time. Solution adds exception handling middleware, so you get nicely formatted exceptions.

Caching

We use Redis caching but you can use in-memory interchangeably with a slight effort.

Enable Elastic Stack

Check Elastic Stack

Extension methods for EF Core in repository

AddFilters, AddSortings, AddPagination, AddFiltersAndPagination, SelectExclusively, ToPaginatedList and so on

TODO: will be elaborated.

Bulk/Batch transaction

If you create your project via AutoCode Solution Generator, you'll get bulkSave method for all of your controllers. If you upload/import an excel file from UI, it'll be handled automatically. Thus, you won't need to struggle with tasks, threads, performance issues and so on.

If you go through boilerplate, find a sample in Admin/AdminSvc/Controllers/UserController.cs

[HttpPost("bulkSave")]
[ClaimRequirement(ActionType.Import)]
public ResponseWrapper BulkSave([FromBody] RequestWithExcelData<SampleModelClass> request)
{
    ResponseWrapper genericResponse = new ResponseWrapper();
    
    if (request != null)
    {
        Task.Run(() => BulkSaveAsync(request, _mainRepository.BulkSave, HttpContext.Request));
        
        genericResponse.Message = DistributedCache.Get(Messages.PROCESS_SUCCESSFUL);
        genericResponse.Success = true;
    }
    else
        genericResponse.Message = DistributedCache.Get(Messages.PROCESS_FAILED);

    return genericResponse;
}

Override some columns/properties' values before writing to DB

All Admin.Data models has 4 properties namely CreatedUserId, CreatedDate, UpdatedUserId, UpdatedDate. They are handled automatically by overriding DBContexts's SaveChanges method.

So you can use the same methodology by overriding DBContextsEx's SaveChanges method. A sample code block is below:

public override int SaveChanges()
{
    if (entity.State == EntityState.Added)
    {
        entity.CurrentValues["CreatedUserId"] = Session.CurrentUser.UserId;
        entity.CurrentValues["CreatedDate"] = DateTime.UtcNow;
    }
    else if (entity.State == EntityState.Modified)
    {
        entity.Property("UpdatedUserId").CurrentValue = Session.CurrentUser.UserId;
        entity.Property("UpdatedDate").CurrentValue = DateTime.UtcNow;
    }
    return base.SaveChanges();
}

How model validation works?

We use FluentValidation which is a strong library to adapt and use easily. In each insert and update method, the request model is validated.

[HttpPost("insert")]
[ClaimRequirement(ActionType.Insert)]
public ResponseWrapper Insert([FromBody] SampleModelClass request)
{
    _sampleValidator.ValidateAndThrow(request);
    return Save(request);
}

Please visit FluentValidation official web site for details.

Set custom multi-language message for a specific control in Validator

...
RuleFor(x => x.TenantType) // Only SystemOwner can assign a new SystemOwner
    .NotEqual(x => (int) TenantType.SystemOwner)
    .When(x => SessionAccessor.GetSession().CurrentUser.TenantType != (int)TenantType.SystemOwner)
    .WithMessage(session => DistributedCache.Get("YOU_CANNOT_ASSIGN_SYSTEM_OWNER_TENANTTYPE_MESSAGE")); // This line provides multi-language message
...

CORS-Origin settings

AllowedCorsOrigins setting is used to allow access for cross-origin requests. This is also be needed if you host your React UI or Microservices in separate servers/domains/ports.

Find appsettings.Development.json and appsettings.Production.json

{
   "ConnectionStrings": {
      "GenesisDB": "User ID=postgres;Password=123456;Host=localhost;Port=5432;Database=GENESIS_DB;",
      "PostgreSQL": "User ID=postgres;Password=123456;Host=localhost;Port=5432;Database=YOUR_DB;"
   },
   "DefaultDatabase": "PostgreSQL",
   "GenesisDBType": "PostgreSQL",
   "ApplicationUrl": "http://0.0.0.0:5051",
   "AllowedCorsOrigins": [
      "http://localhost:3000",
      "http://localhost:5050",
      "http://localhost:5000"
   ]
}

For more app settings, check Configuration.

How to manage and save FILE_UPLOADER's JSON data

By default, posted data for upload components is a json as:

{
    "file": "iVBORw0KGgoAAAANSUhEUgAAAvQAAAISCAYAAACjwVuJAAAgAElEQVR4AeydB5wURfbHf70zG4jiAgossCxZghIERVhJBlDgwHAG1EPgCHreX4mCIlFFghhJCnIIKueJqCgiSthFgpJUctolr2Qkbpr....",
    "fileName":"someImageName.png",
    "mimeType":"image/png"
}

Check How to change posted data

In that case, you should add a serializer to public partial class YourDBContextEx which is in YourMicroserviceName.DataLib

using CoreType.Types;
...
protected override void OnModelCreating(ModelBuilder  modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.Entity<YourModelClass>(entity =>
    {
        entity.Property(e => e.YourFilesColumnName) // Don't forget to change YourFilesColumnName's type to FileContent in DataLib/DBModels/YourModelClass.cs
            .HasColumnName("YourFilesColumnName")
            .HasColumnType("json")
            .HasConversion(
                v => JsonConvert.SerializeObject(v),
                v => JsonConvert.DeserializeObject<FileContent>(v));
            });
}

In DB, you don't need to use BLOB types. If your preferred DB supports JSON data-type (as for PostgreSQL), use it. Otherwise, we recommend nvarchar(max) or text.

Can we choose different DBs for each microservice throughout AutoCode Solution Generation?

Yes, you can specify MSSQL Server for first microservice and PostgreSQL for the other one. It is flexible to choose amongst DB servers, schemas or tables for different microservices.

Throw a custom multi-language error message

throw new GenesisException("SOME_PARAMETER_CODE_FROM_COREPARAMETERS");

If you want to set dynamic variable into the message

 // "Message includes variables as {0} and {1}"
throw new GenesisException("SOME_PARAMETER_CODE_FROM_COREPARAMETERS", SomeVariable1, SomeVariable2);

What happens if I use Where condition and AddFilters, AddFiltersAndPagination extension methods together?

No problem. They will be "AND"ed. Find an example in TenantRepository.cs

public PaginationWrapper<Tenant> List(RequestWithPagination<Tenant> entity)
{
    using (var context = Helper.GetGenesisContext())
    {
        var query = context.Set<Tenant>().AsNoTracking();

        if (Session.CurrentUser.TenantType == (int) TenantType.SystemOwner)
            query = query.IgnoreQueryFilters();

        if (entity.Criteria.StatusFilter != null)
            query = query.Where(x => x.Status == entity.Criteria.StatusFilter);

        var res = query.AddFilters(entity)
                    .OrderBy(x => x.TenantName)
                    .SelectExclusively(x => new { x.TempPassword })
                    .ToPaginatedList(entity);

        return res;
    }
}

How should I proceed to use Model-First/Code-First approach?

You can benefit AutoCode Solution Generator's update method. Check on Genesis Migration

Encrypt-Decrypt method

Find sample in CommunicationManager.cs

string encryptedPassword = EncryptionManager.Encrypt(somePassword);
string decryptedPassword = EncryptionManager.Decrypt(encryptedPassword);

Can I use ToPaginatedList and AddPagination extension methods together?

No need and you should NOT use them together.

Store/persist data as encrypted but decrypt it when fetched

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Microservice.DataLib.DBModels
{
    public partial class SampleModelClass
    {
        [Column("userId")]
        public int UserId { get; set; }

        [Required]
        [Column("userName")]
        [StringLength(50)]
        public string UserName { get; set; }

        [Required]
        [Column("email")]
        [StringLength(80)]
        public string Email { get; set; }

        [Column("password")]
        [StringLength(64)]
        [HashedLogging] // Log the password as hashed
        public string Password { get; set; }

        [EncryptedPersistence] // Store the gender as encrypted since it is subject to GDPR
        [IgnoreLogging] // Don't log the gender
        [Column("gender")]
        public short? Gender { get; set; }

        [HashedPersistence] // Stora data as hashed
        [HashedLogging] // Log as hashed
        [Column("someHashValue")]
        public string SomeHashValue { get; set; }
        ....
    }
}

TODOs

Scheduling Monitoring

Add new controller

Change IdentityServer

Integrate to Active Directory or LDAP

Extended partial classes

DBContextEx and ModelEx

Swagger as documentation

Dependency injection

DTOs

Object AutoMapper

Unit testing

Extending Existing Entities

SignalR integration

Notification sending

Login process

hashed password, call sp, validate and get claims

Last updated on 10/28/2020
← Custom UI PagesUI Questions →
  • Soft delete
  • Sending email and SMS
  • Resource code and Action for authorization
  • Logging
  • Exclude a model/class property from logging
  • Base classes
  • Generic response object
  • EF Core performance
  • Dapper sample
  • Exception Handling
  • Caching
  • Enable Elastic Stack
  • Extension methods for EF Core in repository
  • Bulk/Batch transaction
  • Override some columns/properties' values before writing to DB
  • How model validation works?
  • Set custom multi-language message for a specific control in Validator
  • CORS-Origin settings
  • How to manage and save FILE_UPLOADER's JSON data
  • Can we choose different DBs for each microservice throughout AutoCode Solution Generation?
  • Throw a custom multi-language error message
  • What happens if I use Where condition and AddFilters, AddFiltersAndPagination extension methods together?
  • How should I proceed to use Model-First/Code-First approach?
  • Encrypt-Decrypt method
  • Can I use ToPaginatedList and AddPagination extension methods together?
  • Store/persist data as encrypted but decrypt it when fetched
  • Scheduling Monitoring
  • Add new controller
  • Change IdentityServer
  • Integrate to Active Directory or LDAP
  • Extended partial classes
  • Swagger as documentation
  • Dependency injection
  • DTOs
  • Object AutoMapper
  • Unit testing
  • Extending Existing Entities
  • SignalR integration
  • Notification sending
  • Login process
Copyright © 2021 Net Core Genesis
www.NetCoreGenesis.com