Wednesday, 20 November 2024

Prevent SQL injection

To prevent SQL injection in a C# web application or web API, it's essential to follow best practices for interacting with databases. SQL injection occurs when malicious users insert or manipulate SQL queries to perform unauthorized actions on the database. Here are several strategies you can implement to prevent SQL injection:

  1. Use Parameterized Queries: The parameters are treated as data, not executable code. The database engine knows to treat them as literal values, not as part of the SQL query syntax, preventing SQL injection.
  2. Use ORM Frameworks : usually handle query generation safely and abstract away direct SQL execution, making it harder to introduce SQL injection vulnerabilities.
  3. Use Stored Procedures (With Caution) : Stored procedures can also help mitigate SQL injection risks, but they must be used correctly. Ensure the stored procedure itself is written safely with parameters, not by concatenating values directly into the query.
  4. Validate and Sanitize Input : Ensure that the data entered matches the expected type, expected length and If possible, define a list of allowed values and reject anything that doesn't match. 
  5. Use Web Application Firewall (WAF) : A WAF can detect and block malicious SQL injection attempts and other common web vulnerabilities before they reach your application.
    • Examples: AWS WAF, Azure WAF, Cloudflare WAF.
  6. Use Web API Authentication and Authorization : Ensure that your web API is properly authenticated (using tokens, OAuth, etc.) and authorized (ensuring the caller has appropriate access rights) to prevent unauthorized database access, which could amplify the risk of SQL injection.
  7. Log and Monitor for Suspicious Activity : Monitor your application for unusual query patterns that might indicate an attempted SQL injection attack. Set up logging and alerts for failed login attempts, unusual request patterns, or any attempts to bypass your validation.
  8. SQL Injection Testing and Security Tools : Regularly test your application for SQL injection vulnerabilities using:
    • OWASP ZAP or Burp Suite for security testing.
    • SQLMap for automated SQL injection testing.

The best way to prevent SQL injection in a C# web application is to always use parameterized queries or prepared statements, avoid direct SQL query construction with user input, and leverage ORM frameworks like Entity Framework. Combining input validation, principle of least privilege, and proper security testing can help further mitigate risks.

Continue Reading →

Entity Framework Vs Entity Framework Core

 The main differences between Entity Framework (EF) and Entity Framework Core (EF Core) stem from their architectural changes, cross-platform support, performance improvements, and additional features. Below are the key distinctions:

1. Platform Support:

  • Entity Framework (EF):
    • Primarily designed to run on the .NET Framework, which is Windows-only.
  • Entity Framework Core (EF Core):
    • Cross-platform: Works on Windows, Linux, and macOS. It runs on the .NET Core platform and .NET 5+.

2. Architecture:

  • Entity Framework (EF):
    • EF is tightly coupled with the .NET Framework and its libraries.
  • Entity Framework Core (EF Core):
    • EF Core is a rebuild of the original EF, designed to be lightweight and more modular. It is based on .NET Core and allows for greater flexibility, including easier integration with non-.NET technologies.

3. Performance:

  • Entity Framework (EF):
    • EF had performance issues, particularly with large datasets or complex queries.
  • Entity Framework Core (EF Core):
    • EF Core is generally faster than EF, thanks to a more efficient query pipeline, optimizations in database access, and better memory usage.

4. Features:

  • Entity Framework (EF):
    • Full support for features like Lazy Loading, Complex Types, Stored Procedures, and Model First (designing database schema in the model).
  • Entity Framework Core (EF Core):
    • EF Core introduced several new features but also lacks some features from the older EF version (though many of these have been reintroduced or are in progress). For example:
      • Support for SQL Server, SQLite, PostgreSQL, MySQL, etc.
      • No Lazy Loading by default (but it can be enabled).
      • Better support for LINQ queries, Global Query Filters, and In-memory database.
      • It now supports many-to-many relationships directly, unlike EF which needed manual mapping.
      • Migration system is more flexible.
      • No Model First or Database First support initially (though EF Core now supports some of it).

5. Database Providers:

  • Entity Framework (EF):
    • Limited to the SQL Server provider.
  • Entity Framework Core (EF Core):
    • Multiple database providers are available, including SQL Server, SQLite, PostgreSQL, MySQL, and even non-relational databases like Cosmos DB.

6. Migrations:

  • Entity Framework (EF):
    • Migrations in EF work based on database schema and model synchronization.
  • Entity Framework Core (EF Core):
    • Migrations are better structured and easier to manage in EF Core, especially for modern application requirements.
    • Command-line tools in EF Core make database migrations more manageable.

7. Community and Updates:

  • Entity Framework (EF):
    • EF is in a maintenance-only phase and is not receiving new major updates. It primarily supports applications that are still using .NET Framework.
  • Entity Framework Core (EF Core):
    • EF Core is actively developed and has a strong community around it, with regular updates and new features being added.

8. Compatibility and Support:

  • Entity Framework (EF):
    • Only supports .NET Framework projects, meaning it’s tied to older .NET versions.
  • Entity Framework Core (EF Core):
    • Works with both .NET Core and .NET 5+, making it suitable for modern applications.

9. Query Capabilities:

  • Entity Framework (EF):
    • Has robust querying capabilities, but some features were not as optimized as in EF Core.
  • Entity Framework Core (EF Core):
    • More optimized LINQ queries and better SQL generation.

10. Backward Compatibility:

  • Entity Framework (EF):
    • EF works with older .NET Framework projects but is limited in functionality compared to EF Core.
  • Entity Framework Core (EF Core):
    • Not backward compatible with EF (especially when migrating from EF 6.x to EF Core).

Summary:

  • Entity Framework (EF) is a mature ORM that was built for the .NET Framework and is more feature-rich in some areas but lacks the flexibility and performance improvements of EF Core.
  • Entity Framework Core (EF Core) is a modern, cross-platform ORM that is faster, more modular, and supports a wider variety of databases and deployment scenarios, but it is still catching up with certain features found in EF.

If you're developing new applications or migrating to .NET Core or .NET 5+, EF Core is generally the preferred choice. If you're working with legacy .NET Framework applications, EF may still be the better option.

Continue Reading →

Claim in jwt authentication token

 In JWT (JSON Web Token) authentication, a claim is a piece of information that is encoded within the token. Claims represent statements about an entity (usually the user) and additional metadata. Claims are used to convey information that is relevant to the authentication or authorization process.

A JWT typically contains three parts: the header, the payload, and the signature. The claims are part of the payload.

Types of Claims in JWT

There are three types of claims in a JWT:

  1. Registered Claims: These are predefined claims that are not mandatory but recommended to use for common functionalities. Some of the registered claims include:

    • iss (Issuer): Identifies the principal that issued the JWT.
    • sub (Subject): Identifies the subject of the JWT (usually the user).
    • aud (Audience): Identifies the intended recipient(s) of the JWT.
    • exp (Expiration Time): The expiration time of the JWT, after which it should not be accepted.
    • nbf (Not Before): The time before which the token should not be accepted.
    • iat (Issued At): The time when the token was issued.
    • jti (JWT ID): A unique identifier for the JWT.
  2. Public Claims: These are custom claims that can be defined by anyone, but they should be registered in the IANA JSON Web Token Claims registry or be chosen carefully to avoid conflicts with other claims. These claims often contain information about the user, such as their roles, permissions, or other application-specific data.

  3. Private Claims: These are custom claims created to share information between the parties that agree on them. These are typically not registered or standardized, and they are meant to be used internally between the issuer and the consumer of the JWT.

Example of Claims in JWT Payload

Here is an example of a JWT payload with some claims:

{
    "iss": "example.com",         // Issuer
    "sub": "1234567890",           // Subject (user ID)
    "aud": "exampleApp",           // Audience
    "exp": 1625123456,             // Expiration time (timestamp)
    "iat": 1625113456,             // Issued at (timestamp)
    "role": "admin",               // Custom claim (e.g., user role)
    "username": "john_doe"         // Custom claim (e.g., username)
  }

In this example:

  • iss indicates the issuer of the token.
  • sub identifies the subject (user) of the token.
  • aud specifies the audience for whom the token is intended.
  • exp specifies when the token expires.
  • iat is the timestamp when the token was issued.
  • role and username are private, custom claims used in this specific application.

How Claims Are Used

  • Authentication: Claims like sub (subject) are used to identify the user or entity for which the token was issued.
  • Authorization: Claims like role can be used to check what actions the user is authorized to perform.
  • Token Integrity: Claims like exp (expiration) ensure that the token cannot be used after a certain time.

Claims allow JWT tokens to be versatile and carry various types of information that can be validated and used for access control, personalization, and ensuring the security of the token.


Continue Reading →

What information JWT token contains

JWT (JSON Web Token) is a compact, URL-safe token format used for securely transmitting information between parties. In the context of a Web API, a JWT typically contains three main parts:

1. Header:

  • The header typically consists of two parts:
    • Type: This is usually "JWT" to indicate the token format.
    • Algorithm: The algorithm used for signing the token, such as HS256 (HMAC SHA-256) or RS256 (RSA SHA-256). The algorithm ensures the integrity of the token and prevents it from being tampered with.

Example of a header:

{
    "alg": "HS256",
    "typ": "JWT"
  }

2. Payload:

  • The payload contains the claims or the information being transmitted. There are three types of claims:
    • Registered Claims: These are predefined claims that are recommended to use, but not mandatory. Examples include:
      • iss (Issuer): The entity that issued the token.
      • sub (Subject): The subject or user the token is about.
      • aud (Audience): The intended recipient of the token.
      • exp (Expiration Time): The expiration time of the token (timestamp).
      • iat (Issued At): The timestamp when the token was issued.
      • nbf (Not Before): The timestamp before which the token should not be accepted.
      • jti (JWT ID): A unique identifier for the token.
    • Public Claims: These are claims that can be defined by anyone, but they should be collision-resistant (e.g., using a URI).
    • Private Claims: These are custom claims that are used between the issuer and the consumer (API server). These claims contain application-specific information, such as user roles, permissions, etc.

Example of a payload:

var claims = new[]
{
    new Claim(JwtRegisteredClaimNames.Sub, "userid"), // Registered Claims
    new Claim(JwtRegisteredClaimNames.Name, username), // Registered Claims
    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
    new Claim("role", "admin"), // Custom Claims
    new Claim("email", "johndoe@example.com") // Custom Claims
};

3. Signature:

  • The signature is used to verify that the sender of the JWT is who it says it is and to ensure that the message has not been altered in transit. The signature is created by taking the encoded header and payload, signing it with the secret key (in case of HMAC) or private key (in case of RSA), and appending the result.

Example process of creating the signature:

  • Take the encoded header and payload.
  • Concatenate them with a period (.) separator: header.payload.
  • Sign this concatenated string with the specified algorithm and secret key.
  • Base64Url encode the resulting signature.

The final JWT looks like:

header.payload.signature

Example of a Complete JWT:

A complete JWT token might look like this (note that these parts are base64url-encoded):

eyJhbGciOiAiSFMyNTYiLCJ0eXAiOiAiSl"..."...

Summary:

  • Header: Contains metadata like the algorithm and token type.
  • Payload: Contains the claims or information (such as user ID, roles, etc.).
  • Signature: Ensures the integrity of the token and verifies the sender’s identity.

In the context of Web APIs, JWTs are used to authenticate and authorize users, securely transmitting user data (like a user ID or roles) between the client and the server. The server can verify the authenticity of the JWT and extract relevant information to grant access to resources or perform other actions.



Continue Reading →

JWT Token Generation Code in C#

Install the required NuGet packages

You'll need the following packages in your C# project:

  • System.IdentityModel.Tokens.Jwt (for JWT token generation and validation)
  • Microsoft.IdentityModel.Tokens (for creating signing keys and algorithms)

Set up the JWT Token Generation Code

Here’s an example of how to generate a JWT token in C# using various options:

public string GenerateJwtToken(string username)
{
     // Define JWT claims
    var claims = new[]
    {
        new Claim(JwtRegisteredClaimNames.Sub, "userid"), // Registered Claims
        new Claim(JwtRegisteredClaimNames.Name, username), // Registered Claims
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim("role", "admin"), // Custom Claims
        new Claim("email", "johndoe@example.com") // Custom Claims
    };

    //  Define the security key
    string secretKey = "your_secret_key_here";
    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));

     // Define the signing credentials (HMACSHA256 algorithm)
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

// Create jwt token
    var token = new JwtSecurityToken(
        issuer: "your-issuer", // Can be your app name
        audience: "your-audience", // Can be a target application or service
        claims: claims,
        expires: DateTime.Now.AddMinutes(30),
        signingCredentials: creds);

     // Serialize the token to a string
     var tokenHandler = new JwtSecurityTokenHandler();
     string jwtToken = tokenHandler.WriteToken(token);

    return jwtToken ;
}

Explanation of the Options Used:

  1. Security Key and Signing Credentials:

    • A secret key is used to sign the token. It's crucial to keep this key secure. We use the HMACSHA256 algorithm (SecurityAlgorithms.HmacSha256) to sign the token with the SymmetricSecurityKey.
  2. Claims:

    • Claims are used to include information about the user (e.g., sub, name) and other custom claims (e.g., role, email). Claims are represented as Claim objects.
  3. Issuer and Audience:

    • Issuer: The entity that issued the token, often the name of the app or service that generates the token.
    • Audience: The recipient(s) of the token, often a service or application that will validate the token.
  4. Expiration Time:

    • You can set the expiration time for the token using expires. The token will be invalid once the expiration date is reached.
  5. JWT Token Generation:

    • We use JwtSecurityToken to construct the JWT with all the provided information (issuer, audience, claims, expiration time, and signing credentials).
  6. Serialize the Token:

    • JwtSecurityTokenHandler is used to serialize the JwtSecurityToken object into a string that can be used as the actual JWT token.

3. Customization Options

You can customize the JWT further with the following options:

  1. Audience:

    • If your token is intended for a specific service or client, you can set the audience to that service's identifier.
  2. Signing Algorithms:

    • You can use different signing algorithms like HmacSha256, Rs256, Es256, etc., depending on your use case.

      Example for RSA or ECDSA signing:
      var rsaKey = new RsaSecurityKey(privateKey); // Private RSA key
      var signingCredentials = new SigningCredentials(rsaKey, SecurityAlgorithms.RsaSha256);
  3. Claims: JWT tokens allow you to add custom claims (e.g., roles, permissions, etc.). You can add any additional claim that might be useful for your system's authorization.

  4. NotBefore (nbf): Set the NotBefore claim to indicate that the token is not valid before a certain time: 
    nbf: DateTime.UtcNow.AddMinutes(1)
  5. Issuer & Audience Validation:

    • On the validation side, when you decode the token, you can specify the allowed Issuer and Audience values to ensure that the token is intended for your service.
  6. Example of Token Validation:

    Once you have generated the token, you would typically validate it on the receiving side:

var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
    ValidateIssuer = true,
    ValidateAudience = true,
    ValidateLifetime = true,
    ValidIssuer = "your-issuer",
    ValidAudience = "your-audience",
    IssuerSigningKey = symmetricKey // Same key used to sign the token
};

try
{
    var principal = tokenHandler.ValidateToken(jwtToken, validationParameters,
out SecurityToken validatedToken);
    Console.WriteLine("Token is valid.");
}
catch (SecurityTokenException ex)
{
    Console.WriteLine("Token validation failed: " + ex.Message);
}

Conclusion

By following this approach, you can generate a highly customizable JWT token in C# using multiple options, such as custom claims, signing algorithms, and expiration times. You can further extend this with more advanced features like refreshing tokens, audience validation, and different signing algorithms for more complex security needs.


Continue Reading →

Refresh Token : JWT Token Authentication WEBAPI

 To implement JWT (JSON Web Token) refresh token logic in a Web API, the general idea is to issue two tokens when a user successfully logs in:

  1. Access Token (short-lived): This token is used for authenticating requests and is typically valid for a short duration (e.g., 15 minutes).
  2. Refresh Token (long-lived): This token is used to get a new access token when the old one expires. It typically has a longer expiry time (e.g., 7 days or more).

Steps to Implement JWT Refresh Token in a Web API

1. Login Endpoint (Generate Access and Refresh Tokens)

When the user successfully logs in, generate both an access token and a refresh token. The access token will be used for authentication, and the refresh token will be stored securely (typically in an HttpOnly cookie or in the database).

public class AuthController : ControllerBase
{
    private readonly IConfiguration _configuration;

    public AuthController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    [HttpPost("login")]
    public IActionResult Login([FromBody] LoginRequest request)
    {
        // Validate user credentials
        var user = AuthenticateUser(request.Username, request.Password);
        if (user == null) return Unauthorized();

        // Generate JWT Access Token
        var accessToken = GenerateAccessToken(user);

        // Generate Refresh Token
        var refreshToken = GenerateRefreshToken();

        // Save the refresh token in the database or cache associated with the user

        return Ok(new
        {
            AccessToken = accessToken,
            RefreshToken = refreshToken
        });
    }

    private string GenerateAccessToken(User user)
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, user.Username),
            new Claim(ClaimTypes.NameIdentifier, user.UserId.ToString())
            // Add other claims as necessary
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
_configuration["Jwt:SecretKey"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: _configuration["Jwt:Issuer"],
            audience: _configuration["Jwt:Audience"],
            claims: claims,
            expires: DateTime.Now.AddMinutes(15), // short-lived access token
            signingCredentials: creds
        );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    private string GenerateRefreshToken()
    {
        var randomNumber = new byte[32];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(randomNumber);
        }

        return Convert.ToBase64String(randomNumber);
    }
}

2. Refresh Token Endpoint

When the access token expires, the client sends the refresh token to this endpoint to get a new access token. The refresh token is validated, and if valid, a new access token is issued.

[HttpPost("refresh-token")]
public IActionResult RefreshToken([FromBody] RefreshTokenRequest request)
{
    // Validate the refresh token
    var user = ValidateRefreshToken(request.RefreshToken);
    if (user == null) return Unauthorized();

    // Generate new access token
    var accessToken = GenerateAccessToken(user);

    // Optionally, generate a new refresh token and save it (if you are rotating
// refresh tokens)
    var refreshToken = GenerateRefreshToken();
    SaveRefreshToken(user, refreshToken);

    return Ok(new
    {
        AccessToken = accessToken,
        RefreshToken = refreshToken
    });
}

private User ValidateRefreshToken(string refreshToken)
{
    // Validate the refresh token. Check if it's valid and matches what's stored
// in the database.
    // This could involve checking a database, cache, or other storage for a
// matching refresh token.
    // Implement this logic based on your storage method return GetUserByRefreshToken(refreshToken);
}

3. Refresh Token Storage

Refresh tokens should be securely stored. Options include:

  • HttpOnly Cookies: Secure and less vulnerable to XSS attacks.
  • Database: Store refresh tokens in a table or cache, mapping them to user accounts and ensuring the refresh token is revoked after use.

If you're storing the refresh token in a cookie, set the HttpOnly flag and Secure flag to ensure the token is sent only over HTTPS and cannot be accessed via JavaScript.

Example for setting refresh token in HttpOnly cookie:

Response.Cookies.Append("refresh_token", refreshToken, new CookieOptions
    {
        HttpOnly = true,
        Secure = true,
        SameSite = SameSiteMode.Strict,
        Expires = DateTime.Now.AddDays(7) // Set expiry to match your policy
    });
   

4. Token Expiry and Invalidating Refresh Tokens

Make sure that:

  • Access tokens expire quickly (e.g., in 15 minutes).
  • Refresh tokens can either have a long expiration time (e.g., 7 days) or a single-use (rotating refresh tokens) mechanism to increase security.
  • When a refresh token is used, it’s either revoked or replaced with a new refresh token to avoid token reuse.

5. Middleware for Token Validation

The access token is included in request headers (typically in the Authorization header) as a bearer token. This token is validated with each request to ensure it is still valid.

public class JwtMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IConfiguration _configuration;

    public JwtMiddleware(RequestDelegate next, IConfiguration configuration)
    {
        _next = next;
        _configuration = configuration;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var token = context.Request.Headers["Authorization"].FirstOrDefault()?
.Split(" ").Last();
        if (token != null)
        {
            AttachUserToContext(context, token);
        }

        await _next(context);
    }

    private void AttachUserToContext(HttpContext context, string token)
    {
        try
        {
            var claimsPrincipal = ValidateToken(token);
            context.User = claimsPrincipal;
        }
        catch
        {
            context.User = null;
        }
    }

    private ClaimsPrincipal ValidateToken(string token)
    {
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
_configuration["Jwt:SecretKey"]));
        var tokenHandler = new JwtSecurityTokenHandler();

        var validationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            IssuerSigningKey = key,
            ClockSkew = TimeSpan.Zero,
            ValidIssuer = _configuration["Jwt:Issuer"],
            ValidAudience = _configuration["Jwt:Audience"]
        };

        return tokenHandler.ValidateToken(token, validationParameters, out _);
    }
}

Key Considerations:

  • Secure Storage: Always ensure that refresh tokens are stored securely, either in a database or HttpOnly cookies.
  • Token Rotation: Optionally, rotate refresh tokens to increase security (i.e., issue a new refresh token each time a new access token is issued).
  • Revocation: Implement a strategy for revoking refresh tokens if necessary (e.g., after logout or password change).

This setup should give you a basic implementation of JWT with access token and refresh token in your Web API.


Continue Reading →

Saturday, 16 November 2024

Dynamically passing method : Action, Func delegates

 In C#, you can dynamically pass a method or a delegate to another method by using either delegates or Action/Func types. These approaches allow you to abstract the invocation of methods in a flexible and reusable way.

Let’s look at how to pass methods dynamically using Action (for methods that return void) or Func<T> (for methods that return a value). Both Action and Func are predefined generic delegates in .NET.

Using Action Delegate

  static void Main(string[] args)
  {
    ExecuteAction(printMessage); //Passing method via Action delegate

// Passing an anonymous method (lambda) via Action delegate
    ExecuteAction(() => Console.WriteLine("Hello from Lamda expression"));
  }

// Method that accepts an Action delegate
  static void ExecuteAction(Action action)
  {
      action();
  }
 
// A method that matches the Action delegate signature (void method)
  static void printMessage()
  {
      Console.WriteLine("Hello world");
  }
OUTPUT
  //Hello world
  //Hello from Lamda expression

Using Func<T>Delegate

  static void Main(string[] args)
  {
    int result = ExecuteFunc(getSum, 4, 5);
    int result2 = ExecuteFunc((a, b) => a + b, 5, 6);
    Console.WriteLine(result);
    Console.WriteLine("Result from Lamda: "+result2);
  }

  static int ExecuteFunc(Func<int, int, int> func, int n1, int n2)
  {
      return func(n1, n2);
  }

  static int getSum(int a, int b)
  {
      return a + b;
  }

OUTPUT
  //9
  //Result from Lamda: 11

Key Points:

  • Action<T>: Used for methods that return void.
  • Func<T, TResult>: Used for methods that return a value.
  • You can pass both named methods and lambda expressions (anonymous methods) as delegates.
  • Delegates are type-safe, meaning that they require the method signature to match.

This flexibility lets you dynamically pass any method or lambda expression to another method in C#.


Continue Reading →

Topics

ADFS (1) ADO .Net (1) Ajax (1) Angular (47) Angular Js (15) ASP .Net (14) Authentication (4) Azure (3) Breeze.js (1) C# (53) CD (1) CI (2) CloudComputing (2) Coding (10) CQRS (1) CSS (2) Design_Pattern (7) DevOps (4) DI (3) Dotnet (10) DotnetCore (20) Entity Framework (5) ExpressJS (4) Html (4) IIS (1) Javascript (17) Jquery (8) jwtToken (4) Lamda (3) Linq (10) microservice (4) Mongodb (1) MVC (46) NodeJS (8) React (10) SDLC (1) Sql Server (32) SSIS (3) SSO (1) TypeScript (3) UI (1) UnitTest (2) WCF (14) Web Api (16) Web Service (1) XMl (1)

Dotnet Guru Archives