Saturday, December 31, 2022

.Net Core Eager Loading related data.

As I continue playing around with .Net 6, I stumbled upon to its new feature which is to auto-include related data.

In most cases in app development, data are related and it requires to be included when you try to get the main entity. For instance you have a User table that has a related data to roles, you may want to include the user roles when you fetch the user/s. 

What I previously do is 

var results = await dbSet.Include(c => c.UserRoles).ToListAsync();

With the updates from Microsoft, Now I can add the code below in my DB Context

modelBuilder.Entity<User>().Navigation(e => e.UserRoles).AutoInclude();

That's my year-end post to remind my future self about this feature.

For reference please refer to link below.

https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager#model-configuration-for-auto-including-navigations

Saturday, November 26, 2022

Ianemv Nov 26 & 27 Win Logs

 Angular 14

  • Setup Auth Guard 
  • Secured routes using auth guard

Dotnet

- Usage of Unit-of-Work

  • Creates a wrapper for all repositories so that only the wrapper will be injected to the Program.cs
  • Inject the wrapper to the controller and able to access the specific repository

Design Pattern using Java
  • Visitor Pattern implementation
  • Abstract Factory
  • Factory Pattern


Progress is progress no matter how small it is.


Friday, July 22, 2022

Note : Just bookmarking my .Net 6 journey of resources - July 23 notes

  •  EF Core for M1 : https://stackup.hashnode.dev/ef-migrations-visual-studio-mac
  • Ideally one DbContext for small app
  • To update the model or adding new field steps;
    • Add the field to the model definition
    • Create a migration using Add-Migration (VS), dotnet ef migrations NameOfMigration --context IfManyDBContext
    • Update database by using Update-Database (VS), dotnet ef database update


Will be updated too

Friday, March 18, 2022

Note : .Net 6 / C# Adding timestamp and user created modified to when saving data

As a Laravel user for quite sometime, it's quite easy for me to add User creator, modifier and timestamp for each model.

In the case of .Net 6 (or even earlier) it can be handled at DbContext and creating your base class that has the generic properties for the timestamps (created at, modified at) and user details (created_by / modified_by) 

using System.ComponentModel.DataAnnotations;

namespace YourApp.Models{

    public class BaseEntity

    {

        public DateTime DateCreated { get; set; }

        [Required]

        //id of the user created

        public string UserCreated { get; set; }

        public DateTime? DateModified { get; set; }

        [Required]

        //id of the user edited

        public string UserModified { get; set; }

    }

}


Below is my subclass using the super class BaseEntity

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace YourApp.Models;

/*
 * Patient Model 
 */

public class MyCustomModel : BaseEntity
{
    [StringLength(125)]
    public string FirstName { get; set; }

    [StringLength(125)]
    public string LastName { get; set; }

    [StringLength(125)]
    public string Middlename { get; set; } = string.Empty;

}

So how to automatically do the timestamps and user details? I implemented the tutorial from the blog that I followed here. According to the blog, you can implement it at the override of your DbContext'  SaveChangesAsync.

My DbContext below. I highlighted from the code below the important parts where the magic happens.

using Microsoft.EntityFrameworkCore;
using YourApp.Models;
using System.Security.Claims;

namespace YourApp.Contexts
{
    public class DataContext : DbContext
{
private readonly IHttpContextAccessor _httpContextAccessor;
public DataContext(DbContextOptions <DataContext> options, IHttpContextAccessor httpContextAccessor) : base(options) 
{
_httpContextAccessor = httpContextAccessor;
}

        public DbSet<MyCustomModel > CustomModels{ get; set; }



        protected override void OnModelCreating(ModelBuilder builder)
        {
          base.OnModelCreating(builder);
        }


public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
var insertedEntries = this.ChangeTracker.Entries()
.Where(x => x.State == EntityState.Added)
.Select(x => x.Entity);
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
var currentUsername = !string.IsNullOrEmpty(userId)
? userId
: "Anonymous";

foreach (var insertedEntry in insertedEntries)
{
var auditableEntity = insertedEntry as BaseEntity;
//If the inserted object is an Auditable. 
if (auditableEntity != null)
{

auditableEntity.DateCreated = DateTime.Now;
auditableEntity.UserCreated = currentUsername;
auditableEntity.UserModified = currentUsername;
}
}
var modifiedEntries = this.ChangeTracker.Entries()
.Where(x => x.State == EntityState.Modified)
.Select(x => x.Entity);
foreach (var modifiedEntry in modifiedEntries)
{
//If the inserted object is an Auditable. 
var auditableEntity = modifiedEntry as BaseEntity;
if (auditableEntity != null)
{
auditableEntity.DateModified = DateTime.Now;
auditableEntity.UserModified = currentUsername;
}
}
return base.SaveChangesAsync(cancellationToken);
}

}
}


Aside from that, ensure also from your login that ClaimTypes.NameIdentifier is not empty

var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;

I set my value during login. See login method below.


    [HttpPost]
    [Route("login")]
    public async Task<IActionResult> Login([FromBody] UserLogin model)
    {
      var user = await _userManager.FindByNameAsync(model.Username);
      if (user != null && await _userManager.CheckPasswordAsync(user, model.Password))
      {
        var userRoles = await _userManager.GetRolesAsync(user);

        var authClaims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, user.UserName),
                    new Claim(ClaimTypes.NameIdentifier, user.Id),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                };

        foreach (var userRole in userRoles)
        {
          authClaims.Add(new Claim(ClaimTypes.Role, userRole));
        }

        var token = GetToken(authClaims);

        return Ok(new
        {
          token = new JwtSecurityTokenHandler().WriteToken(token),
          expiration = token.ValidTo
        });
      }
      return Unauthorized();
    }


That pretty much of it. Hope that helps you too.

Tuesday, November 16, 2021

Macbook multiple git config for bitbucket

Bookmarking here how to have a multiple git account in Mac. Similarly, this would also work in Windows machine but it's okay not to have the last part that has UseKeychain part.

Use the config sample below which is found at ~/.ssh/config

Note: Refer to this link to generate rsa keys. 

Host bitbucket.org-user1

    Hostname bitbucket.org
    User git
    IdentityFile ~/.ssh/user_rsa (pub)
    IdentitiesOnly yes

Host bitbucket.org-another
    HostName bitbucket.org-ianemv
    User git
    IdentityFile ~/.ssh/id_rsa
    IdentitiesOnly yes

Host *
    UseKeychain yes
    AddKeysToAgent yes
    IdentityFile ~/.ssh/id_rsa
    IdentityFile ~/.ssh/user_rsa
Source here

On the title, I use bitbucket but this also applies to Github.
Additional resource here.

Thursday, January 28, 2021

Connecting SSH at Hostgator Shared Hosting

 Yes, you read it right! Hostgator. I know it's a bit of conventional but there are still small sites that uses shared hosting such as HostGator.

Anyway, how-to connecting SSH of HostgGator is just around the web and this post is basically my bookmark so I don't have to hunt around the web again.

Key points:

  • At cPanel account find SSH and Manage Keys
  • Add key. Private key usually created automatically.
  • Download Private Key (ppk format)
  • At Kitty / Putty (I'm using Windows) 
    • add host IP address
    • Private key at SSH
    • Set port 2222
  • Connect

Sources:

http://www.velvetblues.com/web-development-blog/configuring-ssh-secure-shell-on-hostgator/

https://www.youtube.com/watch?v=ZmvZy_bzzLs

That's it for now.

Sunday, April 12, 2020

April 6 - 12 Skills Challenge


New stuff learned for the days April 6 to 12, 2020. Quarantine days!


  • Single-SPA
  • Google Cloud Storage
  • Google Kubernetes
  • Google App Engine


Since I've been vacant for quite sometime now, I went to apply for a job last week and given a test challenge for the post. The challenge was to use Single-SPA javascript library. Basically, it's a library that can let you develop an app as microservices and you can use different JS framework or libraries such as VueJS, ReactJS, Vanilla or Angular.

It was cool, I thought it would be easy for me or just a walk in park, by reading the documents I was able to grasp the concept and how everything works. The goal for the challenge test was to learn how to develop an app with Single-SPA and its deployment process.

What I did was a single app approach but still implemented the split application somehow. I have a root config for the app that will load an application when user navigates to a given route. Though, the idea was to split the app by section, like footer, header, sidebar, styling and so on. The idea is to separate each section to have their own repo, which is very convenient for a large team. It's also ideal for migrating one section to another library without affecting the entire app.

Google Cloud Platform.

As I completed doing the basic setup of Single-SPA, it was time to deploy and test it on live site. Using the GCP for the deployment, I now learned how to use its Kubernetes product. And also learned how to use the built docker image, push to Google Container Registry and use it for cluster.

The flow will be

docker build .  ----> build the image
docker tag  --> tag the newly build
docker push  --> push to registry

docker tag <tag_name> <registry_name>/PROJECT_ID/<image_name>

[HOSTNAME] is listed under Location in the console.
It's one of four options: gcr.io, us.gcr.io, eu.gcr.io, or asia.gcr.io.
docker tag ceabfea5e26a gcr.io/boreal-totality-/single_spa_image:test
docker push [HOSTNAME]/[PROJECT-ID]/[IMAGE]
docker push gcr.io/boreal-totality-273505/single_spa_image


And for the last day of the week, April 12, I mostly spent time on Google App Engine and setup my local Kubernetes using minikube and kubectl. With Google App Engine, I just tried to setup a Laravel app. With Laravel requirements, I was able to use SQL service of GCP and have it connect to my Laravel app.

Flutter

My simple test application has been in long hiatus and finally, I was able to revisit and make it work again using the updated version. So I might be able to work on it next week. Btw, I've encountered an error with Flutter http module, I keep getting a 401 response with my login flow. With quick search for help, I found out that the post should have a headers with Content-Type of json

My code snippet would be like this now,

var response = await client.post('$login_path',
                            headers: {"Content-Type": "application/json"},
                            body: json.encode(form));

That's it for now.

StayHome StaySafe