Saturday, December 19, 2020

ASP.net MVC without code!

0) Prepare Database Tables and define relation between tables 

1) Install "EF Core Power Tools" from Extensions



2) Create new MVC Project 



3) Right click on the solution and choose "EF Core Power Tools" > Reverse Engineering


4) Then choose DB and Tables to generate the related classes and DBContext
- Set EntityTypes Path= Models
- Set DBContext Path= Data
- Check Data Annotation
- Check Include Connection String
- Check Install EF Core Packages



Notes:

After create DBContext, you can use it direct like this

            var _db = new NopcommerceContext();
            var Lang = _db.Country.ToList();

But it is better to add it to dependency injection using start class ConfigService method.


5) Open \"Startup.cs">"ConfigureServices" add "DBContext" Service

and Update "appsettings.json" add "ConnectionStrings"



Notes:
After define DBContext in dependency injection, we can access DB using any controller like this

    public class LanguagesController : Controller
    {
        private readonly NopcommerceContext _context;

        public LanguagesController(NopcommerceContext context)
        {
            _context = context;
        }

   }


6) Right click on "Controller" folder and choose "Add new Controller", from the new menu choose "MVC using Entity Framework"


8) Choose class and DBContext and ControllerName then click OK




9) Now you have CURD functionality on the selected tables but you may need to apply one modification if the field is lookup

In Details and Delete update field instead of view Number, it should view the Value 
In Create and Edit, it should view drop down









Friday, December 18, 2020

C# .Net Tips (Start from "Core" 5.0)

 

 Fat Arrow when define property

public override double TotalSpecialPower => 1000;

Is equivalent to:

public override double TotalSpecialPower
{
    get { return 1000; }
}
 

And this (note the parentheses):

public override double TotalSpecialPower() => 1000;

Is equivalent to:

public override double TotalSpecialPower() { return 1000; }
 

 

Check Null

 string product = null; // compiler error - this is a non-nullable type
string? product = null; // no error - this is a nullable type

 

string relatedName = p?.Related?.Name;
? means check if null, and continue if not null other, set the value to null.


string relatedName = p?.Related?.Name ?? "<None>";
decimal? price = p?.Price ?? 0;

?? if value is null replace with new value

 

 

MultipleActiveResultSets=True

 "ConnectionStrings": {
"SportsStoreConnection": "Server=(localdb)\\MSSQLLocalDB;Database=SportsStore;MultipleActiveResultSets=true"
}

 

When create a reader on a connection (ExecuteReader), then for every row in that reader, perform another command (with ExecuteNonQuery). In this case you should use MultipleActiveResultSets=True

Multiple Active Result Sets (MARS) was added specifically for this type of operation so that you don't have to have two connections open at the same time to be able to read from a SqlDataReader AND execute additional batches. 

 

 

Executing raw SQL with Entity Framework Core 5

_context.Language.FromSqlRaw("SELECT * FROM Language")

_context.Language.FromSqlInterpolated($"SELECT * FROM Language where id={id}")

_context.Database.ExecuteSqlInterpolated($"delete from Language where id={id}");

Notes:

- FromSqlInterpolated is similar to FromSqlRaw but allows you to use string interpolation syntax.  

- When executing Update/Delete it return the number of affected rows, no need to use DbSet, and use DbContext.Database.ExecuteSqlRaw


Left Join using EF (Include, and ThenInclude)

var blogs = context.Blogs .Include(blog => blog.Posts) .Include(blog => blog.Owner) .ToList();

 

  var blogs = context.Blogs .Include(blog => blog.Posts) .ThenInclude(post => post.Author)

 

var blogs = context.Blogs .Include(blog => blog.Posts) .ThenInclude(post => post.Author) .ThenInclude(author => author.Photo) .Include(blog => blog.Owner) .ThenInclude(owner => owner.Photo)

 

 How to execute general sql using ADO.net

  1. using (var command = context.Database.GetDbConnection().CreateCommand())
  2. {
  3. command.CommandText = "SELECT * From Table1";
  4. context.Database.OpenConnection();
  5. using (var result = command.ExecuteReader())
  6. {
  7. // do something with result
  8. }
  9. }

 

Return one scaler value from Database using  raw SQL

1) update context class define new class Scaler that has one column "Value"
2)  update OnModelCreatingPartial and let this new class has no key


 


 Now, set the required result as 'Value' (The same property name in the class)



 

Dependency injection

 Design Pattern can use to pass object (inject object) to any class instead of define it locally in every class, this give us advantage to create a single instance of that class or even choose different implementation when user point to abstract class



Once we register a service, the IoC container automatically performs constructor injection if a service type is included as a parameter in a constructor. 

 

Service Lifetime

1) Singleton which creates a single instance throughout the application. It creates the instance for the first time and reuses the same object in the all calls.
memory leaks in these services will build up over time.
also memory efficient as they are created once reused everywhere.
Use Singletons where you need to maintain application wide state.
Application configuration or caching of data is some of the examples where you can use singletons.

2) Scoped lifetime services are created once per request within the scope. It is equivalent to a singleton in the current scope.
For example, in MVC it creates one instance for each HTTP request, and uses the same instance in the same web request.
better option when you want to maintain state within a request.

3) Transient lifetime services are created each time they are injected or requested. best for lightweight, stateless services.
since they are created every time, it will use more memory & Resources and can have the negative impact on performance
use this for the lightweight service with little or no state.
Transient objects are always different; a new instance is provided to every controller and every service.

 


 

How to Enable  Session?

 Update ConfigureServices , Configure


 


 

 How to call Session after enable it?

use HttpContext.Session.SetString, and HttpContext.Session.GetString



RedirectToAction with parameter


    How to Send Data from View to Layout template?

    in View

    @section Calender{

    ..........................

    }

    in Layout

     @await RenderSectionAsync("SectionName1", required: false)


    How to Send Data from Controller to View?

    1.         ViewData["VariableName"] = "Value";  
    2.         ViewBag.VariableName = "VariableName";  
    3.         TempData["VariableName"] = "VariableName"
    4.         return View(ViewModelDataObject);

     

    1) ViewData needs casting in the view if the value not string

    2) ViewBag is strong type and does not need this casting 

    3) TempData can maintains its value even if you redirect the request to new page, but ViewBag,ViewData lose the values after redirect!, Note: TempData is one time use

    4) ViewModel send data using View and receive it using @Model






    ========================== ViewBag ============================




    ============================ ViewData ===============================





    ========================= Model ===============================



    How to allow Controller Value to run as HTML in View?

    @Html.Raw( ControllerValue)

     

    How to  Convert ID to drop down <Select> during Create/Edit?

    In Controller
     var _VacationTypeslist = _context.VacationTypes.ToList();
     _VacationTypeslist.Insert(0, new VacationTypes { VacationTypeId = 0, VacationType = "Select" });
     ViewBag.VacationTypeslist = new SelectList(_VacationTypeslist, "VacationTypeId", "VacationType");


    In View

    <select asp-for="ProjectId" asp-items="@ViewBag.VacationTypeslist" class="form-control"></select>      


     Create Dynamic Dropdown using jQuery


    In Controller

            public JsonResult GetUsersList(string FullName)
            {
                var Users = _context.ProjectTeam.Where(x => x.FullName.Contains(FullName)).OrderBy(y => y.FullName).ToList();
                Users.Insert(0, new ProjectTeam { TeamMemberId = 0, FullName = "------- " + Users.Count + " items found-------" });
                var results = Json(new SelectList(Users, "TeamMemberId", "FullName"));
                return results;
            }

     

    In View

    <script type="text/javascript">
        function ApplySearch(SearchText)
        {
            var url = '@Url.Content("~/")' + "ControllerName/GetUsersList";
            $.getJSON(url, { FullName: SearchText }, function (data) {
                var items = '';
                $.each(data, function (i, item) {
                        items += "<option value='" + item.value + "'>" + item.text + "</option>";
                    });
                $('#UserId').html(items);
            });
        }
    </script>

    <select required id="UserId"  name="UserId" class="form-control">
         <option value="0">----- Search Results -----</option>
    </select>



    Post form to specific controller


    <form asp-action="ChangePassword" asp-controller="Manage" method="post">
        ...
    </form>







    Receive Posted Form in Controller

    IFormCollection will contains all submited fields

    [HttpPost]
     public async Task<IActionResult> Create(IFormCollection collection)
    {

    }




    Important Model Annotation


    # define DB column name
    [Column("AttendID")]

    # define DB column type
    [DataType(DataType.Date)]

    # define Javascript error message
    [Required(ErrorMessage = "Please choose profile image")]

    # define HTML field label
    [Display(Name = "Profile Picture")]

    #for password field, we can make it hidden in web api
    [JsonIgnore]

    #for password, we can define it to be hidden in notmal form
    [DataType(DataType.Password)]

    Decimal is the best datatype for money, and we can use like this

        [Column(TypeName = "decimal(18,2)")]
        public decimal Price { get; set; }



    Develop MVC Login using Identity

    0)  add on all controllers [Authorize]  annotation
    1) Write login logic

      [HttpPost]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> Login(Login userModel)
            {
                try
                {
                    if (ModelState.IsValid)
                    {
                        var user = _context.AdminAccounts.Where(x => (x.email == userModel.LoginName || x.PhoneNumber== userModel.LoginName) && x.Password == userModel.UserPassword.PW_Encrypt()).FirstOrDefault();
                        if (user == null)
                        {
                            //Add logic here to display some message to user    
                            ViewBag.Message = "Invalid Credential";
                            return View("Index", userModel);
                        }
                        else
                        {
                            //Initialize a new instance of the ClaimsIdentity with the claims and authentication scheme    
                            var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
    
                            identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, Convert.ToString(user.AdminAccountsID)));
                            identity.AddClaim(new Claim(ClaimTypes.Name, Convert.ToString(user.FirstName+" "+ user.LastName)));
                            //identity.AddClaim(new Claim("anykey", "any value"));
    
                            //Initialize a new instance of the ClaimsPrincipal with ClaimsIdentity    
                            var principal = new ClaimsPrincipal(identity);
    
                            //SignInAsync is a Extension method for Sign in a principal for the specified scheme.    
                            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties()
                            {
                                IsPersistent = false       
                            });
    
                            return RedirectToAction("Index", "Home");
                        }
                    }
                    else
                    {
                        return View("Index");
                    }
                }
                catch (Exception ex)
                {
                    return View("Index");
                }
            }
    

    2) Update startup.cs, function ConfigureServices, add the next line

                services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(x => x.LoginPath = "/login/index");

    3) Update startup.cs, function Configure, add the next line before "app.UseAuthorization();"

    app.UseAuthentication();



    Develop WebAPI Login using JWT

    1) add on all controllers [Authorize]  annotation except on login controller
    2) add the next method to login API

            [HttpPost]
            public async Task<ActionResult<Candidates>> PostAsync(string UserID, string Password)
            {
                try
                {
                    var loginUser = _context.Candidates.Where(x => x.PhoneNumber == UserID && x.Password == Password.PW_Encrypt()).FirstOrDefault();
                    if (loginUser == null)
                    {
                        return NotFound();
                    }
                    else
                    {
                        var Claims = new List<Claim>
                            {
                                new Claim("type", "Admin"),
                                new Claim(ClaimTypes.Name, loginUser.FirstName+" "+loginUser.LastName),
                                new Claim(ClaimTypes.NameIdentifier, loginUser.CandidateID.ToString()),
                                new Claim(ClaimTypes.Email, loginUser.email+""),
                            };
    
                        var Key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("rafiesKyNUyvGbnHs7ke2NxxxzQzNLW7mPmHbnZZ"));
    
                        var Token = new JwtSecurityToken(null,null,
                            Claims,
                            expires: DateTime.Now.AddDays(1.0),
                            signingCredentials: new SigningCredentials(Key, SecurityAlgorithms.HmacSha256)
                        );
    
                        var CurrentToken = "Bearer " + new JwtSecurityTokenHandler().WriteToken(Token);
                        return new OkObjectResult(new { CurrentToken, loginUser });
                    }
    
    
                }
                catch (Exception ex)
                {
                    return NoContent();
                }
            }
    



    2) Update startup.cs, function ConfigureServices, add the next line

                var TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    //ValidIssuer = "https://getswivel.co.uk",
                    //ValidAudience = "https://getswivel.co.uk",
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("rafiesKyNUyvGbnHs7ke2NxxxzQzNLW7mPmHbnZZ")),
    ClockSkew = TimeSpan.Zero // remove delay of token when expire }; services .AddAuthentication(options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(cfg => { cfg.TokenValidationParameters = TokenValidationParameters; }); services.AddAuthorization(cfg => { cfg.AddPolicy("Admin", policy => policy.RequireClaim("type", "Admin")); cfg.AddPolicy("Agent", policy => policy.RequireClaim("type", "Agent")); });




    3) Update startup.cs, function Configure, add the next line before "app.UseAuthorization();"

    app.UseAuthentication();


    ============================================

    using policy-based authorization

    after login in MVC controller

    ClaimsIdentity identity = null;
                    identity = new ClaimsIdentity(new[]
                    {
                        new Claim(ClaimTypes.Role, user[0].Type)
                    });
    

    here is the Authorization setup in startup.cs configurationServices method

    services.AddAuthorization(options =>
            {
                options.AddPolicy("OwnerOnly", policy => policy.RequireClaim(ClaimTypes.Role,"Owner"));
                options.AddPolicy("AdminOnly", policy => policy.RequireClaim(ClaimTypes.Role, "Admin"));
                options.AddPolicy("UserOnly", policy => policy.RequireClaim(ClaimTypes.Role, "User"));
    
            });
    

    and the controller

    [Authorize(Policy = "OwnerOnly")]
        public IActionResult NewDepartment()
        {
            return View();
        }








    Fix CORS in web API

    1) Add [Microsoft.AspNetCore.Cors.EnableCors] annotation to all web APIs controllers
    2) Update startup.cs, function ConfigureServices, add the next line

    services.AddCors();

    3) Update startup.cs, function Configure, add the next line after "app.UseRouting();" and before "app.UseEndpoints"

                app.UseCors(builder =>
                {
                    builder
                    .AllowAnyOrigin()
                    .AllowAnyMethod()
                    .AllowAnyHeader();
                });




    How Web API can return string or JSON string as IActionResults?

    public IActionResult Temp() {
    return Content("Hi there!");
    }


    How to create function for string encryption as a property of any string? 

    StringExtension static class can use to extend string properties. 

        public static class StringExtension
        {
            public static string PW_Encrypt(this string input)
            {
                using MD5 md5Hash = MD5.Create();
                byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
                StringBuilder sBuilder = new StringBuilder();
    
                for (int i = 0; i < data.Length; i++)
                {
                    sBuilder.Append(data[i].ToString("x2"));
                }
                return sBuilder.ToString();
            }
        }
    



    Now, after define this class, we can use string encryption like this:

    var x="Hiiiiiiiiiiiiiiiiiiiiii";

    var My_encriptStringValue= x.PW_Encrypt();










    WEB API Error

    when we have Model like this, StaffMember refare to Department and Department refare back to StaffMember then you try to return first object including the second object, you will got error !


    when write a web api that return StaffMember and related Department, we got the next error

    web api System.Text.Json.JsonException: A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles.

    Code Sample that can case a problem
                    var loginUser = _context.StaffMember.Where(x => x.PhoneNumber == UserID && x.Password == Password.PW_Encrypt())
                        .Include(x => x.Department).FirstOrDefault();

    Solution

    just add [System.Text.Json.Serialization.JsonIgnore] on the child object when try to refare back to the main object 

    Option 2 is literally a little statement in the Startup class, in the ConfigureServices method (this is for ASP.NET Core 6+):

    services.AddControllers().AddJsonOptions(x =>
                    x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
    

    If you use ASP.NET Core 5:

    the default JSON serializer does not support reference loop handling.

    In order to handle that issue, you'll have to first install the older JSON serializer (used in older versions of .NET Core), Microsoft.AspNetCore.Mvc.NewtonsoftJson in the Nuget package manager.

    The usage is pretty simple:

    services.AddMvc().AddNewtonsoftJson(o => 
    {
        o.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
    });
    

    Or like this if you are using a simple web API:

    services.AddControllers().AddNewtonsoftJson(o => 
    {
        o.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
    });


    How to ignore POCO properties if it contains NULL?
    just add the next attribute to the POCO property 

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]







    On .net core web api, How to create controller with three get methods like:

    GET /movies
    GET /movies?name={name}
    Get /Movies/{Id}

    without got the error "The request matched multiple endpoints."

    solution:

            [HttpGet]
            public async Task<IActionResult> Get()
            {
                return Ok();
            }

            [HttpGet("GetByName/{name}")]
            public async Task<IActionResult> GetByName(string name)
            {
                return Ok();
            }

            [HttpGet("GetById/{Id}")]
            public async Task<IActionResult> GetById(Guid Id)
            {
                return Ok();
            }  




    How to create ef core where in multi-line?


     var result = _context.Jobs.Include(x => x.JobQuestions).AsQueryable();

     if (JobType != null)
                    result = result.Where(x => x.JobType == JobType);

     if (SalaryPer != null)
                    result = result.Where(x => x.SalaryPer == SalaryPer);

             

    return await result.ToListAsync();





    How to Add Swagger to exist MCV project to document current APIs?


    Install the next 4 packages using Nuget package manager:
    Microsoft.OpenApi
    Swashbuckle.AspNetCore.Swagger
    Swashbuckle.AspNetCore.SwaggerGen
    Swashbuckle.AspNetCore.SwaggerUI


    On Startup.cs apply the next 3 steps

    1) using Microsoft.OpenApi.Models;

    2) Under ConfigureServices at last add

                services.AddSwaggerGen(c =>
                {
                    c.SwaggerDoc("v1", new OpenApiInfo { Title = "swivelAPIs", Version = "v1" });
                    /*
                    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                    {
                        In = ParameterLocation.Header,
                        Description = "Please enter JWT with Bearer into field",
                        Name = "Authorization",
                        Type = SecuritySchemeType.ApiKey
                    });
                    c.AddSecurityRequirement(new OpenApiSecurityRequirement {
                        { new OpenApiSecurityScheme
                                {
                                    Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer"}
                                },
                            new string[] {}
                        }
                        });
                    */
                });



    3) Under Configure add on beginning

                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("v1/swagger.json", "swivelAPIs v1"));