Wednesday, February 22, 2017

Best Practices


01) use jquary class for module wise

example :-
                             
                      module Ecart.User.ViewInfo{
                             var m =  (()=>{
                                 var apiCalls ={
                                        get(e):{
                                             return $.ajax({});
                                        },
                                       insert(e):{
                                         return $.ajax({});
                                      }
                                },
                                function get(e?){
                                    if(!validateForm()){return;}
                                    apiCalls.get(e).done((r)=>{console.log(r);}).fail((r)=>{alert('failed');});
                                }
                               function insert(e?){
                                   if(!validateForm()){return;}
                                   apiCalls.insert(e).done((r)=>{console.log(r);}).fail((r)=>{alert('fail');});
                               }

                             function validateForm(){
                                    return true;
                             }
                                  return{
                                        int:()=>{
                                             // int controlls for this module.
                                            // call crud functions.
                                             $('#btnClick').on('click',this,insert);
                                        },
                                       
                                 }
                            });

                       $(document).ready(m.init);
                 }

02) Think Web api method as Noun. not as the verb.

      public class StudentController:ApiController{
           
          [HttpGet]
          [Route("Students")]
          public IHttpResponse Get(){}

          [HttpGet]
          [Route("Students/{id}")]
          public IHttpResponse Get(int id){}

          [HttpPost]
          [Route("Student}s")]
          public IHttpResponse Post(Student obj){}

          [HttpDelete]
          [Route("Students/{id}")]
          public IHttpResponse Post(int id){}

          [HttpPut]
          [Route("Students")]
          public IHttpResponse Put(Student object){}
  
     }
        
in that case use plural except singular for identity routing 

03) User Automapper to Domain transfer to Different Domain.


using X.App_Start
public class AutomapperConfig(){
     public void Register(){
       // register mapping
    }
}

// call to mapping from Globle.ascx
public class Globle{
  
     public void Application_Start(){
       // call to registration
       new AutomapperConfig().Register();
    }
}


04) Always return IHttpResponse type from Api method

          public IHttpResult Get(){
              try{
                   if(!Validate()){
                       return BadRequest('error list converted to json');
                    }
                    else{ 
                       return Ok<T>(result); 
                     }
                    }catch(Exception e){
                return Content<Exception>(HttpStatusCode.InternalServerError,e);
              }
         }

05) Be Always Asyncronas 

          public async Task <IHttpResult> Insert(T object){
                 await c.Insert(T); 
                 return Ok();
         }


06) Always do Server side validation.


     public static bool IsModelValied(object x, out List<string> e)
        {
            ValidationContext context = new ValidationContext(x, null, null);
            IList<ValidationResult> errors = new List<ValidationResult>();
            var ex = new List<string>();
            bool isValied = true;
            if (!Validator.TryValidateObject(x, context, errors, true))
            {
                isValied = false;
                foreach (ValidationResult result in errors)
                {
                    ex.Add(result.ErrorMessage);
                }
            }
            e = ex;
            return isValied;
        }
              

   public Task<IHttpResponse> Insert(T obj){

         var err = new List<string>();
          if(!IsModelValied(T,out err)){
             return BadRequest(JSON.Stringify(err));
           }
         return Ok();
    }

07) Use Dependency injection 

Controller 

public class StudentService:ApiController{

      StudentDbService service;
      public A(UnitOfWork _uow){
          serice = new StudentDbServive(_uow);
      }

}

08) Use Lamda Expression what you need

public void Insert(){

     var lst = new List<T>();
     Action<string> come = (e)=>{ lst.Add(e); };

     Predicate<string> =passwordLength= (e)=>{ return e.length > 10; }
    
     Func<string,string>  some2 = (e) =>{ return e.string();}
}


09) use Unit of work.


public interface IUnitOfWork
   {
       GenericRepository<Student> StudentRepository { get; }
       GenericRepository<Subject> SubjectRepository { get; }
       GenericRepository<StudentSubject> StudentSubjectRepository { get; }
       GenericRepository<UserAuthontication> UserAuthonticationRepository { get; }
       void Save();
       Task SaveAsync();
       DbContext Context { get; }
 
   }
   public class UnitOfWork : IUnitOfWorkIDisposable
   {
       public UnitOfWork(IContext cnt, Enums.ERunType type)
       {
           if (type == Enums.ERunType.Debug)
           {
               context = (SchoolContext)cnt;
               context.Configuration.AutoDetectChangesEnabled=false;
           }
           else
           {
               context = (MockDbContext)cnt;
           }
       }
       private DbContext context;
       private GenericRepository<Student> studentRepository;
       public DbContext Context
       {
           get
           {
               return context;
           }
       }
       public GenericRepository<Student> StudentRepository
       {
           get
           {
 
               if (this.studentRepository == null)
               {
                   this.studentRepository = new GenericRepository<Student>(context);
               }
               return studentRepository;
           }
       }
public void Save()
        {
            context.SaveChanges();
        }
        public async Task SaveAsync()
        {
            await context.SaveChangesAsync();
        }
        private bool disposed = false;
 
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    context.Dispose();
                }
            }
            this.disposed = true;
        }
 
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }


10) Use Genaric repository


public class GenericRepository<TEntitywhere TEntity : class
  {
      internal DbContext context;
      internal DbSet<TEntity> dbSet;
 
      public GenericRepository(DbContext context)
      {
          this.context = context;
          this.dbSet = context.Set<TEntity>();
      }
 
      public virtual IEnumerable<TEntity> Get(
          Expression<Func<TEntitybool>> filter = null,
          Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
          string includeProperties = "")
      {
          IQueryable<TEntity> query = dbSet;
 
          if (filter != null)
          {
              query = query.Where(filter);
          }
 
          foreach (var includeProperty in includeProperties.Split
              (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
          {
              query = query.Include(includeProperty);
          }
 
          if (orderBy != null)
          {
              return orderBy(query).ToList();
          }
          else
          {
              return query.ToList();
          }
      }
 
      public virtual TEntity GetByID(object id)
      {
          return dbSet.Find(id);
      }
 
      public virtual void Insert(TEntity entity)
      {
          dbSet.Add(entity);
      }
 
      public virtual void Delete(object id)
      {
          TEntity entityToDelete = dbSet.Find(id);
          if (entityToDelete!= null)
          {
              Delete(entityToDelete);
          }
      }
 
      public virtual void Delete(TEntity entityToDelete)
      {
          if (context.Entry(entityToDelete).State == EntityState.Detached)
          {
              dbSet.Attach(entityToDelete);
          }
          dbSet.Remove(entityToDelete);
      }
 
      public virtual void Update(TEntity entityToUpdate)
      {
          dbSet.AddOrUpdate(entityToUpdate);
      }
  }


11. Be Interface Every ware

Api base interface

public interface IService<T>  
   {
       Task<IHttpActionResult> Insert(T item);
       Task<IHttpActionResult> Delete(int Id);
       Task<IHttpActionResult> Update(T item);
       Task<IHttpActionResult> Get();
       Task<IHttpActionResult> GetSingle(int studentId);
   }
public class StudentServiceController : BaseServiceControllerIService<StudentViewModel>
  {

PlutoContext

   public interface IContext
    {
        DbSet<Student> Students { get; set; }
        DbSet<Subject> Subjects { get; set; }
        DbSet<StudentSubject> StudentSubjects { get; set; }
        DbSet<UserAuthontication> UserAuthontications { get; set; }
    }
 
   public class SchoolContext : DbContext,IContext 
   {


Genatic repository

public interface IRepositoryRead<Twhere T : class
   {
       IEnumerable<T> Get(Expression<Func<Tbool>> filter = null,
           Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
           string includeProperties = "");
       T GetByID(object id);
   }
 
   public interface IRepositoryUpdate<Twhere T : class {
       void Insert(T entity);
       void Delete(object id);
       void Delete(T entityToDelete);
       void Update(T entityToUpdate);
   }
 
   public interface IRepositoryUpdateAsync<Twhere T : class {
 
       Task<int> InsertAsync(T entity);
       Task DeleteAsync(object id);
       Task UpdateAsync(T entityToUpdate);
   }

public interface IStudentDbService : IRepositoryRead<StudentBo>, IRepositoryUpdateAsync<StudentBo>
    {
        IEnumerable<StudentBo> Get(Expression<Func<Studentbool>> filter = nullFunc<IQueryable<Student>, IOrderedQueryable<Student>> orderBy = nullstring includeProperties = "");
    }

public class StudentDbService : IStudentDbService
   {
.............................


12) Use cache everywhere


using System.Runtime.Caching;
public class MemoryCacher
    {
        public static bool Add(string key, object value, DateTimeOffset absExpiration)
        {
            MemoryCache memoryCache = MemoryCache.Default;
            return memoryCache.Add(key, value, absExpiration);
        }
 
        public static object GetValue(string key)
        {
            MemoryCache memoryCache = MemoryCache.Default;
            return memoryCache.Get(key);
        }
         
        public static void Delete(string key)
        {
            MemoryCache memoryCache = MemoryCache.Default;
            if (memoryCache.Contains(key))
            {
                memoryCache.Remove(key);
            }
        }
    }

in this case put all cache keys in one place like CacheKeysConfig.cs as const string.

13). Use Json Format


config.Formatters.JsonFormatter.SupportedMediaTypes
.Add(new MediaTypeHeaderValue("text/html"));

14) Use Following Folder structure



     X (Api,Utility/)
     Lib /
          X.Domain
          X.Bo -->Utility(Enums)
          X.DbAccess --> Context / Migration
          X.DbService --> Service / Interface / Infastucture
     Test / X.UnitTest,X.AutomationTest,X.MockData


15) use attribute base routing for.

01) api versioning.
02) web api method as noun

[RouterPrefix('v1')]
public class StudentController:ApiController{

[Route("student")]
       public async Task<IHttpActionResult> Get()
       {}
[Route("student/{Id:int}")]
       public async Task<IHttpActionResult> GetSingle(int Id)
       {}
[Route("student")]
        public async Task<IHttpActionResult> Insert(StudentViewModel item)
        {}

[Route("student")]
        public async Task<IHttpActionResult>  Update(StudentViewModel item)
        {}

[Route("student/{Id:int}")]
       public async Task<IHttpActionResult> Delete(int Id)
       {}
}

     

16) use observer to bind values to html.


17) use template for itoration date


18) Use GZip To compress result

https://damienbod.com/2014/07/16/web-api-using-gzip-compression/
https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression
   


19) KISS (Keep It Simple and Stupid)


  1. do not expose more than expert.
  2. return thing in similar way.
    public async Task<IHttpResponse> Response(){
         return Ok<T>(value);
         return BadRequest(string content);
         return Component(ResponseCode.InternalServerError,Ex);
    }
  3. use only common Response Code. not all. 404/200/401/500/403

20) Documentation 


21) Consider about Versioning

       [RouterPrefix('api/v2')]
       public class StudentController : ApiController{}



22) Result Filtering / Sorting / Searching / Pagination



    quay string  GET /tickets?fields=id,subject,customer_name,updated_at&state=open&sort=-updated_at&skip=10&take=100




23) Errors and Exception Handling


return json message format 
{ "code" : 1024, "message" : "Validation Failed", "errors" : [ { "code" : 5432, "field" : "first_name", "message" : "First name cannot have fancy characters" }, { "code" : 5622, "field" : "password", "message" : "Password cannot be blank" } ] }





24) use Customer action filter always

public XController:ApiController{ [Validation] public async Task<IHttpResponse> Insert(Student s){ return null; } } public ValidationActionFilter:ActionFilter{ public override void OnActionExecuting(HttpContext x){ //check context model state is valied if(!x.ModelState.IsValied()){ //set context as bad request x.Response = new HttpResponseMessage<Json>("some validation failded",badRequest); } } } // finally this action filter must register on Globle.asax public static void Configure(config){ config.Filter.Add(new ValidationActionFilter()); }

25) User Following exception type for following reasons.

406 - notAcceptable (not trigger on database (primary key violation))
404 - not found
200 - OK
201 - created
403 - Forbidden
400 - bad request

26 Handle sql exception (Primary key | forign key) by following way

custom exception for db
public class DbPKViolationException:Exception
    {
        public DbPKViolationException() : base() { }
        public DbPKViolationException(string message) : base(message) { }
    }
    public class DbFKViolationException : Exception
    {
        public DbFKViolationException() : base() { }
        public DbFKViolationException(string message) : base(message) { }
    }
    public class DbUniqKeyViolationException : Exception
    {
        public DbUniqKeyViolationException() : base() { }
        public DbUniqKeyViolationException(string message) : base(message) { }
    }

genetic exception handler

protected Exception HandleException(Exception exception)
       {
           var x = exception;
           while (x.InnerException != null)
           {
               x = exception.InnerException;
           }
           var sqlException = x as SqlException;
           if (sqlException==null)
           {
               throw x;
           }
           if (sqlException.Number == 2627 || sqlException.Number == 2601)
           {
               throw new DbPKViolationException();
           } 
           throw exception;
       }
   }


Service class : base service


public UserBo GetByID(object id)
       {
           try
           {
               return Mapper.Map<UserBo>(this.uow.UserRepository.GetByID(Convert.ToUInt32(id)));
           }
           catch (Exception ex)
           {
               throw HandleException(ex);
           }
       }


Controller class

[HttpPost]
[Route("user/login")]
public async Task<IHttpActionResult> Login(UserViewModel entity)
{
    try
    {
        var o = this.service.Login(Mapper.Map<UserBo>(entity));

        return Ok();
    }
    catch (ArgumentException)
    {
        return Content<string>(HttpStatusCode.NotAcceptable, "invalied user name or password");
    }
    catch (Exception ex)
    {
        return await LogErrors(ex);
    }
}




No comments:

Post a Comment

Sql server row level policy