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 : IUnitOfWork, IDisposable { 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<TEntity> where 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<TEntity, bool>> 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 : BaseServiceController, IService<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<T> where T : class { IEnumerable<T> Get(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = ""); T GetByID(object id); } public interface IRepositoryUpdate<T> where T : class { void Insert(T entity); void Delete(object id); void Delete(T entityToDelete); void Update(T entityToUpdate); } public interface IRepositoryUpdateAsync<T> where 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<Student, bool>> filter = null, Func<IQueryable<Student>, IOrderedQueryable<Student>> orderBy = null, string 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)
- do not expose more than expert.
- return thing in similar way.
public async Task<IHttpResponse> Response(){
return Ok<T>(value);
return BadRequest(string content);
return Component(ResponseCode.InternalServerError,Ex);
} - 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