Saturday, August 15, 2020

xUnit for Unit Testing

xUnit works on both .NET Core and .NET framework projects.

If you are planning to work with .NET Core, then there is a project template in VS 2019. Else if what you have is a .NET framework class library as your unit testing project then you need to install the below two Nugets.











            Common Asserts:

Assert.Equal()
Assert.NotEqual()
Assert.InRange()
Assert.True()
Assert.False()
Assert.Contains()
Assert.DoesNotContain()
Assert.All(collection, item => Assert.False(string.NUllOrWhiteSpace(item)))

Some other Asserts would be:

Assert.IsType<ObjectType>(actualResultingObject);
Assert.IsAssignableFrom<ObjectType>(resultingObject); //To check if the object is of particular type
Assert.Same(objectOne, objectTwo); //Check if two objects are the same
Assert.Throws<ArgumentNullException>(() => obj.Create(null));

In xUnit, unlike NUnit or MSTest, the test class is not attributed. xUnit simply finds public methods in the DLL which are attributed with [Fact] or [Theory]

Structure of a Test method:

class EmployeeShould
{
    [Fact]
    public void GetEmployeeNameById_ShouldReturnCorrectName()
    {
         EmployeeFactory employees = new EmployeeFactory() //Arrange
         var actualValue = employees.GetEmployeeById(001); //Act
         Assert.Euqal("ExpectedName", actualValue); //Assert
    }
}

Categorizing tests
To caegorize test you could use the Trait[] attribute.

Example:
    [Fact]
    [Trait("Category","Employee")]
    public void GetEmployeeNameById_ShouldReturnCorrectName()
    {
    }

You can view the Categories in the Test explorer view. You can also run the categorized test in the command-line interface;

Example: dotnet test --filter Employees

Skipping a test
Example:
    [Fact(Skip = "Dont need to run this test at the moment")]
    public void GetEmployeeNameById_ShouldReturnCorrectName()
    {
    }

Including additional test output text.
For this we need to implement the xUnit's ITestOutputHelper interface

Example:
using Xunit.Abstractions;

public class EmployeeShould
{
    private readonly ITestOutputHelper _output;

    public EmployeeShould(ITestOutputHelper output)
    {
        _output = output;
    }

    [Fact]
    public void GetEmployeeNameById_ShouldReturnCorrectName()
    {
         _output.WriteLine("Starting Employee Test");

         EmployeeFactory employees = new EmployeeFactory();//Arrange
         var actualValue = employees.GetEmployeeById(001); //Act
         Assert.Equal("ExpectedName", actualValue); //Assert
    }
}

You can view the output in the Test explorer. Also when you execute through the command-line, you can view the TRX file result.

Example:  dotnet test --filter Employees --logger:trx

Setup and disposing after test run
Create a constructor in the test class to do the setting up and implement IDisposable interface to dispose.

Example:
public class EmployeeShould : IDisposable
{
    private readonly ITestOutputHelper _output;

    public EmployeeShould(ITestOutputHelper output)
    {
       _employees = new EmployeeFactory(); //Create the object to be used in all test methods 
        _output = output;
    }

    [Fact]
    public void GetEmployeeNameById_ShouldReturnCorrectName()
    {
         _output.WriteLine("Starting Employee Test");

         var actualValue = _employees.GetEmployeeById(001); //Act
         Assert.Euqal("ExpectedName", actualValue); //Assert
    }

    public void Dispose()
    {
        _output.WriteLine("Disposing all EmployeeShould Test class resources");
    }
}

Sharing context between tests
You can use a Fixture class for this purpose. Then implement xUnits IClassFixure interface
Example:

public class CompanyState 
{
    public List<Employee> Employees= new List<Employee>();
    //Add method to load employees from repo

   public void UpgradeEmployeeDesignation()
   {
   }

    public Employee GetEmployeeById(int id)
   {
   }
}

//create new fixture class
public class EmployeeFixture : IDisposable
{
    public CompanyState State { get; private set; }
 
    public EmployeeFixture()
    {
          State = new CompanyState();
    }
   
    //Disposing resources
    public void Dispose()
    {
        //cleanup
    }
}

//implementing the IClassFixture interface in your test class
using Xunit.Abstractions;

public class EmployeeShould : IClassFixure<EmployeeFixture > 
{
    private readonly EmployeeFixture _employeeFixture;
    private readonly ITestOutputHelper _output;

    //constructor
    public EmployeeShould(EmployeeFixture employeeFixtureITestOutputHelper output)
    {
       _employeeFixture = employeeFixture;
        _output = output;
    }

    [Fact]
    public void GetEmployeeNameById_ShouldReturnCorrectName()
    {
         _output.WriteLine("Starting Employee Test");
     
         _employeeFixture.State.UpgradeEmployeeDesignation();  //update employee config across org before test
         var actualValue = _employeeFixture.State.GetEmployeeById(001); //Act
         Assert.Equal("ExpectedDesignation", actualValue.Designation); //Assert
    }
}

Sharing context across multiple test classes
Step 1) Create a fixture class.
Step 2)  Create a new class which implements xUnits ICollectionFixture interface.

Example:
[CollectionDefinition("EmployeeState Collection")]
public class CompanyStateCollection : ICollectionFixture<EmployeeFixture>
{}

Notice here that the CollectionDefinition Attribute is used.

Step 3) Using the collection in the test classes

Example:
//Test class one
[Collection("EmployeeState Collection")]
public class TestClass1
{
    private readonly EmployeeFixture _employeeFixture;
    private readonly ITestOutputHelper _output;

    //constructor
    public EmployeeShould(EmployeeFixture employeeFixtureITestOutputHelper output)
    {
       _employeeFixture = employeeFixture;
        _output = output;
    }

    [Fact]
    public void Test1()
   {}

    [Fact]
    public void Test2()
   {}
}

//Second test class
[Collection("EmployeeState Collection")]
public class TestClass2
{
    private readonly EmployeeFixture _employeeFixture;
    private readonly ITestOutputHelper _output;

    //constructor
    public EmployeeShould(EmployeeFixture employeeFixtureITestOutputHelper output)
    {
       _employeeFixture = employeeFixture;
        _output = output;
    }

    [Fact]
    public void Test3()
   {}

    [Fact]
    public void Test4()
   {}
}

Notice here we have only attributed the collection in test class, and not implemented any interface.

Data Driven Testing


Data driven testing comes in handy for scenarios where the same test method has to be executed for different data inputs.

Example:
Lets say there is a class called Student and it has properties to store the score for each subject. A method to get student grade by deriving the median of all subjects. and another method to get the overall semester pass of fail status though another method. And lets say we need to test the semester pass or fail status. A sample test method for the above mentioned scenario would be something like below:

[Theory]
[InlineData(67,68,69)]
[InlineData(100,100,100)]
[InlineData(100,80,90)]
public void ExamResult_StudentGetsHighMarks_ShouldPassSemester(int math, int science, int english, boolean result)
{
    // test code
}

Theory attribute tells that this test method will be executed multiple times and the InlineData attribute provide the various test data scenarios. The test explorer shows three test cases for the above test method.

How to share test data across tests

In the below sample i will store the test data in a new class file

public class StudentTestData
{
   public static IEnumerable<object[]> TestData
   {
       get
      {
          yield return new object[] {67, 68, 69};
          yield return new object[] {100,100,100};
          yield return new object[] {100,80,90};
      }
   }
}

Now we can update the test method

[Theory]
[DataMember(nameof(StudentTestData.TestData), MemberType = typeof(StudentTestData))]
public void ExamResult_StudentGetsHighMarks_ShouldPassSemester(int math, int science, int english, boolean result)
{
    // test code
}

Reading test data from a file such as a CSV

Sample csv file content:

67,68,69
100,100,100
100,80,90

Create a class to read external test data file:

public class ExternalTestData
{
   public static IEnumerable<object[]> TestData
   {
      get
     {
          string[] csvLines = File.ReadAlLines("TestData.csv");
          var testCases = new List<object[]>();

           foreach(var csvLine in csvLines)
           {
              IEnumerable<int> values = csvLine.Split(',').Select(int.Parse);
              object[] testCase =  values.Cast<object>().ToArray();
              testCases.Add(testCase);
           }
            return testCases;
     }
   }
}

update test method references:

[Theory]
[DataMember(nameof(ExternalTestData.TestData), MemberType = typeof(ExternalTestData))]
public void ExamResult_StudentGetsHighMarks_ShouldPassSemester(int math, int science, int english, boolean result)
{
    // test code
}

If you get an error when you try to run the test such as "File Not Found Exception". Then right click on the CSV file in the project. Go to properties of the CSV. UNder General settings, select "Copy always" in the "Copy to output directory" setting.

Creating Custom Data Source Attributes

We will create a new class which implements xUnits DataAttribute

using Xunit.Sdk;

public class StudentDataAttribute : DataAttribute
{
   public override IEnumerable<object[]> GetData(MethodInfo testMethod)
   {
       yield return new object[] { 67, 68, 69 };
       yield return new object[] { 100, 100, 100 };
       yield return new object[] { 100, 80, 90 };
   }
}

Then update the test method,:

[Theory]
//[DataMember(nameof(ExternalTestData.TestData), MemberType = typeof(ExternalTestData))]
[StudentData]
public void ExamResult_StudentGetsHighMarks_ShouldPassSemester(int math, int science, int english, boolean result)
{
    // test code
}

If you are new to xUnit and have been using other unit testing frameworks you can get an easy comparison between the frameworks by  clicking on this link

Sunday, August 9, 2020

"Could not import package" error when importing a bacpac file from Azure Sql database

 When importing a bacpac file from Azure SQL server instance to your local SQL Server database instance, you might come across this error


One of the causes are that the database compatibility may be mismatched between the two databases. For an example when you "extract a Data tire application" from a SQL instance in Azure, it may be not compatible with SQL Server 2012 instance. Make sure that source and destination databases compatibility levels are compatible.

Visit this link for a set of SQL Server database compatibility levels.


Saturday, August 8, 2020

EntityFramework Core : Transaction execution issue when using In-Memory database

The EntityFramework In-Memory database does not support Transactions as of this writing. Therefore you might get the error: 

Error generated for warning 'Microsoft.EntityFrameworkCore.Database.Transaction.TransactionIgnoredWarning: Transactions are not supported by the in-memory store. See http://go.microsoft.com/fwlink/?LinkId=800142'. This exception can be suppressed or logged by passing event ID 'InMemoryEventId.TransactionIgnoredWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.


To solve this issue, you need to ignore the Transaction Warning when EF is Configured.

 iWebHostBuilder.ConfigureServices(services =>
{
                   services.AddDbContext<IvitDbContext>(options =>
                {
                    options.UseInMemoryDatabase("InMemoryDb");
                    //In memory db does not support transactions. Hence ignore transaction warnings
                    options.ConfigureWarnings(x => x.Ignore(InMemoryEventId.TransactionIgnoredWarning));
                });
});


Saturday, June 27, 2020

Back to basics with JavaScripts

Remembering a bit of coolness in JavaScripting...
Using JavaScript, we can set a links or a input buttons (etc...) event to a JS function by only using JavaScript rather than adding code in the control filed attribute. This is pretty cool.

Example:

<html>
<head>
    <script type="text/javascript">

    /*Wait for the page to load first*/
    window.onload = function () {
        var siteLink = document.getElementById("siteLinkId");

/*run when the link is clicked*/
        siteLink.onclick = function () {
            if (!alert("Make sure you are connected to the VPN")) {
                window.location.href = "https://test.mywebsite.com";
            }
            return false;
        }
    }
    </script>
</head>
<body>
    <a id="siteLinkId" href="http://www.google.com">Visit My Site</a>       
</body>
</html> 

Tuesday, March 24, 2020

Integrating Serilog to ASP.NET Core, Log to a rolling file and Removing the log noise

In this example I am using .NET Core 3.1 version which has LTS from Microsoft.

1) Installing Serilog

 Get Serilog to your solution using the Nuget package "Serilog.AspNetCore".























2) Removing the default logging

In the appsettingss.json you will get a configuration setting section for the built in logging which gets created for each ASP.NET Core project. Since we are not using it anymore we can remove that section.

  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }

3) Adding Serilog to the solution

In the program.cs file set the Serilog configurations

public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
   .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
   .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
   .Enrich.FromLogContext()
   .WriteTo.File("C:\\ProgramData\\MyWebApplication\\MyLog.txt",
  rollOnFileSizeLimit: true,
  fileSizeLimitBytes: 2000,
  retainedFileCountLimit: 5)
   .CreateLogger();
try
{
Log.Information("Starting up");
CreateHostBuilder(args).Build().Run();

}
catch (Exception ex)
{
Log.Fatal(ex, "Application start-up failed");
}
finally
{
Log.CloseAndFlush();
}
}

You may have to use the following using directives:

using Serilog;
using Serilog.Events;
using Serilog.Formatting.Compact;

Now, specify that you will be using serilog when the HostBuilder is created.

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});

4) Add logger to your controller class

Example:

    [Route("api/[controller]/[action]")]
    [ApiController]
    public class MySampleController : ControllerBase
    {
        private readonly ILogger<MySampleController> _logger;

        //constructor
        public MySampleController (
            ILogger<MySampleController> logger)
        {
            _logger = logger;
        }

     //sample Action
        [HttpPost]
        public async Task<ActionResult<Payload>> PostEmployeeInfo([FromBody]JObject notification)
        {
           try
          {
                _logger.LogInformation("Post Employee Info Success for :{0} {1}", paramOne, paramTwo);
           }
          catch(Exception ex)
         {
               _logger.LogError("Error - Post Employee: Message: {0} StackTrace: {1}", ex.Message, ex.StackTrace);
          }
        }
    }

How is the rolling file implemented ?

In the above code we have already specified it in the Log configuration section. See below code block:

.WriteTo.File("C:\\ProgramData\\MyWebApplication\\MyLog.txt",
  rollOnFileSizeLimit: true,
  fileSizeLimitBytes: 2000,
  retainedFileCountLimit: 5) 

This states that we are rolling the files based on a maximum file size limit. In this example I have specified the size to be only 2KB, But in a practical scenario a 5MB file size would be fine. I have set the number of rolling files to 5. So there will be a maximum of 5 files at a given time. The file containing the oldest logs will be removed when a new file is created.

How is the Noise removed ?

Serilog has many log levels:

Level
Usage
Verbose
Verbose is the noisiest level, rarely (if ever) enabled for a production app.
Debug
Debug is used for internal system events that are not necessarily observable from the outside, but useful when determining how something happened.
Information
Information events describe things happening in the system that correspond to its responsibilities and functions. Generally these are the observable actions the system can perform.
Warning
When service is degraded, endangered, or may be behaving outside of its expected parameters, Warning level events are used.
Error
When functionality is unavailable or expectations broken, an Error event is used.
Fatal
The most critical level, Fatal events demand immediate attention.
[Reference: GitHub Serilog Configuration Basics]

Normally you would set the logs minimum level to Information.

Example:
Log.Logger = new LoggerConfiguration()
       .MinimumLevel.Information()

There is a catch here. Serilog starts to log all Information level logs. It logs information generated related to AspNetCore. I also have the Entity Framework Core in the solution. So now I also get Information level logs generated regarding EntityFrameworkCore. A sample log would look like:



But we don't need all this noise in he log all the time. So we have to specify it when we configure the logging. The following lines overrides the default settings.

   .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
   .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)

It sets the logs generated regarding AspNetCore  and EntityFrameworkCore to a higher level which is "Warning". So you would get only the warning and Fatal information from these two components. Now the log will be clearer and you can easily spot the information logs which you place in the Controller using the _logger.LogInformation() method.

Resulting file structure:
MyLog_003.txt
MyLog_004.txt
MyLog._005txt
MyLog_006.txt
MyLog_007.txt

Note that the initial log created will be like "MyLog.txt". Then increment as MyLog_001.txt. As the log files are rolling it will increase the file numbering and remove the older files.

Saturday, March 14, 2020

ASP.NET Core - The requested page cannot be accessed because the related configuration data for the page is invalid

Scenario:

You have installed the .NET Core SDK in your computer. Debugs in the Visual Studio are working fine. Then you publish the solution to a folder location in IIS. Then you get the below error:

Error:

The requested page cannot be accessed because the related configuration data for the page is invalid 

HTTP Internal Server Error 500.19

Error code 0x8007000d

Fix:

Install the Windows hosting bundle from this link: https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-aspnetcore-3.1.1-windows-hosting-bundle-installer

[Note: The version 3.1.1 is current as of this writing]

Installation:



Voilà !! Your ASP.NET Core application starts to work in IIS

Saturday, March 7, 2020

Create a simple Login using CookieAuthentication in ASP.NET Core

I am using Core 3.1

1) Configure Services in the Startup.cs

public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(o => o.LoginPath = new PathString("/Home/Login"));
}

2) Add Configuration in the Startup.cs

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseAuthentication();
}

3) Create a LogIn action method in the Controller

Here the user login information is received through a ViewModel

[HttpPost]
public ActionResult Login(LoginViewModel user)
{
if (ModelState.IsValid)
{
if (!string.IsNullOrEmpty(user.UserName) && !string.IsNullOrEmpty(user.Password))
{
var userId = _configuration.administrator.UserName;
var userPassword = _configuration.administrator.Password;

if (user.UserName == userId && user.Password == userPassword)
{
var claims = new[] { new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Role, "Administrator") };

var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(identity));

return RedirectToAction("Index", "MyBusinessController");
}
else
{
TempData["LogInMessage"] = "LogIn failed. Incorrect username or password";
return RedirectToAction("Login");
}
}
}
return View(user);
}

4) Display the Logged in user probably in the _Layout.cshtml

                    @if (Context.User.Identities.Any(i => i.IsAuthenticated))
                    {
                        <div class="nnav navbar-nav" style="padding:10px 15px;float: right;text-align: left;color:#9d9d9d;padding-top: 15px;">
                        Welcome @Context.User.Identity.Name
                                    @Html.ActionLink("(Logout)", "Logout", "Home")
                        </div>
                    }

5) Logout action method
[Authorize]
public ActionResult Logout()
{
HttpContext.SignOutAsync(
CookieAuthenticationDefaults.AuthenticationScheme);
return RedirectToAction("Login");
}

Sunday, March 1, 2020

Adding swagger to your ASP.NET Core Project

1) Installation
Install the Nuget Package "Swashbuckle.AspNetCore" to your project.







2) Add swagger services to middleware

Add the following code in the startup.cs classes ConfigureServices method

public void ConfigureServices(IServiceCollection services)
       {
services.AddSwaggerGen( c => {
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
});
                }

3) Enable swagger

In the configure method add the below code

            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
           {
                        //Enable to serve swagger as a JSON end point
app.UseSwagger();
                        //Enable to serve the swagger UI
app.UseSwaggerUI(c => {
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
            }

4) Configure the Debug launch
In the project properties, Debug section under the "Launch browser" add the URL
Now when you start debugging the browser will open up the swagger for you.



Tuesday, February 25, 2020

The JSON value could not be converted to System.Collections.Generic.IEnumerable`1[Newtonsoft.Json.Linq.JToken]

If you are getting the following error in your .Net Core Project
"The JSON value could not be converted to System.Collections.Generic.IEnumerable`1[Newtonsoft.Json.Linq.JToken]"
Then you have to install the "Microsoft.AspNetCore.Mvc.NewtonsoftJson" Nuget package.

Afterwards, in the startup class, update Startup.ConfigureServices to call AddNewtonsoftJson.
Example:

public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews().AddNewtonsoftJson();
  }

Friday, February 7, 2020

Entitty Framework Core - Migrations

To migrate through the package manager console download the Nuget "Microsoft.EntityFrameworkCore.Tools"

The Enable-migration is deprecated in Core, so no need of this command as the migrations are enabled by default.


Adding a migration
Example : Add-Migration MyFirstMigration

Update the database
update-database

To get a SQL script containing all migration snapshots use the below command:
 Script-Migration

For more help on EF Core migrations type in the below command in Package manage console
 Get-Help entityframework

Thursday, January 30, 2020

Extension Methods

Extension methods allow you to inject additional methods without modifying, deriving or recompiling the original class, struct or interface.

An extension method is actually a special kind of static method defined in a static class. To define an extension method, define a static class.

The first parameter of the extension method specifies the type on which the extension method is applicable. So the first parameter must be preceded with the this modifier.

Extension methods can be recognized in Visual Studio Intellisense as shown below



Example :  In the below example I am performing multiplication on a integer by calling the Multiply() extension method. then I do a division. Here before dividing the two numbers I check if the divisor is positive by using the isPositive() extension method, then do the division by using the Division() extension method

1) static class containing Extension Methods

    public static class Extensions
    {
        public static int Multiply(this int number, int multiplier)
        {
            return number * multiplier;
        }

        public static bool isPositive(this int number)
        {
            return number > 0;
        }

        public static double Division(this int dividend, int divisor)
        {
            return dividend / divisor;
        }
    }

2) class to test the extension methods

    class ExtensionMethodsTest
    {
        /// <summary>
        ///  Usage one - Multiplication
        /// </summary>
        public void GetMultiplier()
        {
            int i = 5;
            int result = i.Multiply(10);
            Console.WriteLine(result);         
        }

    /// <summary>
    /// usage two - Division
    /// </summary>
    public void GetDivisor()
        {
            int dividend = 256;
            int divisor = -1;
            double? quotient = null;

            if (divisor.isPositive())
            {
                quotient = dividend.Division(divisor);
            }
            else
            {
                Console.WriteLine("Divisor should be a positive number");
            }
            Console.ReadLine();
        }
    }

3) calling the ExtensionMethodsTest class

            ExtensionMethodsTest test = new ExtensionMethodsTest();
            test.GetMultiplier();
            test.GetDivisor();

Result:

Wednesday, January 29, 2020

Renaming the wwwroot folder - .NET Core

In .NET Core, only the files with in the web root are served unlike in .NET. All the other files are blocked. So, you have to have all JavaScript, CSS and image files within the web root which by default is the wwwroot folder. Lets say you renamed the "wwwroot" folder to "Content", then you have to set the new name as the web root. Set it inside the main method of the Program.cs file.

public class Program
{
    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseWebRoot("Content")
            .UseIISIntegration()
            .UseStartup<MyStartup>()
            .Build();

        host.Run();
    }
}

Saturday, January 25, 2020

Run query inside a Transaction - Entity Framework

In Entity Framework you have to run the code with in a DbContextTransaction scope. In this example we run within the DbContextTransaction scope, Get a lock & finally commit at the end.

public bool MySampleMethod(Model instance)
{
using (DbContextTransaction scope = _context.Database.BeginTransaction())
{
bool sent;
_context.Database.ExecuteSqlCommand("SELECT TOP 1 Id FROM MySampleTable WITH (TABLOCKX, HOLDLOCK)");

sent = _context.MySampleTable.Any(n => n.Guid == instance.Guid &&
n.PublishedDate ==
instance.PublishedDate &&
n.UserId == instance.UserId &&
n.RegistrationId ==
instance.RegistrationId);

if (!sent)
{
DoFurtherProcessing(instance);
}
scope.Commit();
}
return sent;
}

Tuesday, January 7, 2020

AddSingleton vs AddScoped vs AddTransient methods - Dependency injection into controllers in .NET Core

The Model class

public class InventoryItems
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}

The interface for the service class

public interface IInventoryServices
{
InventoryItems AddInventoryItems(InventoryItems items);
Dictionary<string, InventoryItems> GetInventoryItems();
}

The service class

public class InventoryServices : IInventoryServices
{
private readonly Dictionary<string, InventoryItems> _inventoryItems;

public InventoryServices()
{
_inventoryItems = new Dictionary<string, InventoryItems>();
}
public InventoryItems AddInventoryItems(InventoryItems items)
{
_inventoryItems.Add(items.Name, items);
return items;
}

Dictionary<string, InventoryItems> IInventoryServices.GetInventoryItems()
{
return _inventoryItems;
}
}

The controller which the service is injected in the constructor

    [Route("api/[controller]")]
    [ApiController]
    public class InventoryController : ControllerBase
    {
private readonly IInventoryServices _service;

public InventoryController(IInventoryServices services)
{
_service = services;
}

[HttpPost]
[Route("AddInventoryItems")]
public ActionResult<InventoryItems> AddInventoryItem(InventoryItems items)
{
var inventoryItems = _service.AddInventoryItems(items);
return inventoryItems;
}

[HttpGet]
[Route("GetInventoryItems")]
public ActionResult<Dictionary<string, InventoryItems>> GetInventoryItems()
{
var inventoryItems = _service.GetInventoryItems();
return inventoryItems;
}
    }

Now in the Startup.cs we have to specify the dependency injection under the "ConfigureServices" method.

public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<IInventoryServices, InventoryServices>();
}

There are three ways in which you could set the Dependency Injection. They are AddSingleton(), AddScoped() and AddTransient()

Singleton - An instance of the service class (InventoryServices) is created, when the interface of the service (IInventoryServices) is first requested and that single instance of the service class (InventoryServices) is used by all http requests throughout the application

Scoped - Here we get the same instance within the scope of a given http request but a new instance of the service is created during a different http request

Transient - a new instance of the service class is created every time an instance is requested whether it is the scope of the same http request or across different http requests 

Auxiliary
Postman Post Request:









  

                                                                                                                                               
  
             Postman Get Request:



Monday, January 6, 2020

This Project is configured to use SSL. Disable https in visual studio web project - Asp.Net Core

In the .NET Core WEb applications SSL are enabled by default.

You get a message as "This Project is configured to use SSL. To avoid SSL warnings in the browser you can choose to trust the self-signed certificate that IIS Express has generated. Would you like to trust the IIS SSL certificate?"

Even though it is recommended to have SSL, during debugging you may need to disable this option.

For that, Go to project properties. In the "Debug" tab, find the "Web Server Settings" section. Un-tick the "Enable SSL" option.