Thursday, June 1, 2017

[ASP.NET Core] Dependency Injection service lifetime

 ASP.NET Core   Dependency Injection   Lifetime


Introduction


We can inject built-in framework services or our services in ASP.NET Core by configuring the container's services in Startup.cs:  ConfigureServices method.

See more information on Dependency Injection in As

We will learn the lifetime of the four injection ways,

1.  Transient
New instance is provided to every controller and every service.

2.  Scoped
Service are created once per request.

3.  Singleton
Single instance throughout the application, lazy singleton.

4.  Singleton Instance
Create instance when registered, eager singleton.



Environment


.NET Core 2.0.0 preview 1



Sample


Create custom services

Here we will create several interfaces to identify Transient, Scoped, Singleton, Singleton-instance services.

Service interface

public interface IGuidService
{
    string Title { get; set; }
    Guid Id { get; set; }
}

public interface IGuidServiceTransient : IGuidService
{}
public interface IGuidServiceScoped : IGuidService
{}
public interface IGuidServiceSingleton : IGuidService
{}
public interface IGuidServiceSingletonInstance : IGuidService
{}


Service class

public class GuidService : IGuidService, IGuidServiceTransient, IGuidServiceScoped, IGuidServiceSingleton, IGuidServiceSingletonInstance
{
    public GuidService(string title, Guid? guid = null)
    {
        this.Title = title;

        if (guid != null && !guid.Equals(Guid.Empty))
            this.Id = (Guid)guid;
        else
            this.Id = Guid.NewGuid();

        //Leave a log when the service instance is created
        LogUtility.Logger.Warn($"{title} : completed constructor.");
    }

    public string Title { get; set; }
    public Guid Id { get; set; }

}



Register services

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    //Transient
    services.AddTransient<IGuidServiceTransient>(provider => new GuidService(title: "Transient"));
    //Scoped
    services.AddScoped<IGuidServiceScoped>(provider => new GuidService(title: "Scoped"));
    //Singleton
    services.AddSingleton<IGuidServiceSingleton>(provider => new GuidService(title: "Singleton"));
    //Singleton instance
    services.AddSingleton<IGuidServiceSingletonInstance>(new GuidService("Instance", Guid.Empty));
}




Inject services to see the lifetime

We will log the GUID when receiving a request.

Controller

public class DIdemoController : Controller
    {
        private IGuidServiceTransient _guidServiceTransient = null;
        private IGuidServiceScoped _guidServiceScoped = null;

        private IGuidServiceSingleton _guidServiceSingleton = null;
        private IGuidServiceSingletonInstance _guidServiceSingletonInstance = null;

        public DIdemoController(
            IGuidServiceTransient guidServiceTransient,
            IGuidServiceScoped guidServiceScoped,
            IGuidServiceSingleton guidServiceSingleton,
            IGuidServiceSingletonInstance guidServiceSingletonInstance
             )
        {
            this._guidServiceTransient = guidServiceTransient;
            this._guidServiceScoped = guidServiceScoped;
            this._guidServiceSingleton = guidServiceSingleton;
            this._guidServiceSingletonInstance = guidServiceSingletonInstance;
        }

        public IActionResult Index()
        {
            _logger.Debug($"Action: Index");

            _logger.Info($"Transient : {this._guidServiceTransient.Id}");
            _logger.Info($"Scoped : {this._guidServiceScoped.Id}");
            _logger.Info($"Singleton : {this._guidServiceSingleton.Id}");
            _logger.Info($"Instance : {this._guidServiceSingletonInstance.Id}");
            return View();
        }

}


Result on first and second request



The result shows that the Singleton service was created only ONCE on demand (receiving a request), thus the two requests get the same GUID.
However, Transient and Scoped services were created every time we send a request.
But what the difference between Transient and Scoped ones? We will talk about it later on the next demo.

Notice that we didn’t have any construction log with the Singleton-Instance service, why? The reason is that the Singleton-Instance service was created when we registered it on this line:
services.AddSingleton<IGuidServiceSingletonInstance>(new GuidService("Instance", Guid.Empty));

And thaz why I called it an eager singleton.
However my log utility was initialized after the above code, so we didn’t get any log when it was created :- )


Inject services to tell the difference between Transient and Scoped

Let’s modify the MVC controller to inject multiple services on Transient and Scoped.

Controller

public class DIdemoController : Controller
    {
        private IGuidServiceTransient _guidServiceTransient = null;
        private IGuidServiceScoped _guidServiceScoped = null;

        private IGuidServiceTransient _guidServiceTransientDup = null;
        private IGuidServiceScoped _guidServiceScopedDup = null;

        public DIdemoController(
            IGuidServiceTransient guidServiceTransient,
            IGuidServiceScoped guidServiceScoped,
             IGuidServiceTransient guidServiceTransientDup,
             IGuidServiceScoped guidServiceScopedDup)
        {
            this._guidServiceTransient = guidServiceTransient;
            this._guidServiceScoped = guidServiceScoped;
            this._guidServiceTransientDup = guidServiceTransientDup;
            this._guidServiceScopedDup = guidServiceScopedDup;
        }

        public IActionResult Index()
        {
            _logger.Debug($"Action: Index");

            _logger.Info($"Transient : {this._guidServiceTransient.Id}");
            _logger.Info($"Scoped : {this._guidServiceScoped.Id}");
            _logger.Info($"Transient(2) : {this._guidServiceTransientDup.Id}");
            _logger.Info($"Scoped(2) : {this._guidServiceScopedDup.Id}");
            return View();
        }

}


Result



We found that Scoped service was only created once! And Transient service was created on every injection. So the request got the same GUID on Scoped service.


Summary


How we choose the right way to inject our service? Here are some suggestions.
1.  Use Transient on stateless service.
2.  Use Scoped to ensure the same instance in a request.
3.  Use Singleton to keep or deal with application level information, but it should be a thread-safe service.
4.  Use Singleton-Instance to create instance on Application startup for application level usage.



Reference




[.Net Core] dotnet commands


 .NET Core   dotnet CLI   


Basic


Add --help on the following commands for more information.

dotnet new

Install project template pack

$ dotnet new –install|[-i] <template name>

Or uninstall

$ dotnet new –uninstall|[-u] <template name>



See all project templates

$ dotnet new --list


Create new project

$ dotnet new <project template name> [--name] 

For example,
dotnet new mvc --name JB.Sample.Mvc



dotnet sln

Create a new solution file

dotnet new sln --name <solution file name (without .sln)>


For example,

dotnet new sln --name MyDemo 



Add a project to solution

$ dotnet sln [solution file path] add <project name>.csproj



For example,

dotnet sln add MyDemo/MyDemo.csproj 

Or

dotnet sln MyDemo.sln add MyDemo/MyDemo.csproj



Remove a project from solution

$ dotnet sln remove <project name>.csproj


See projects which are included in the solution

$ dotnet sln list




dotnet restore

$ dotnet restore


dotnet build

$ dotnet build


Build with specific configuration

$ dotnet build --configuration Release


Build with specific configuration

$ dotnet build --framework netcoreapp3.1



Project modification



package

Add package

$ dotnet add package <package name> [--version]

For example,
dotnet add package System.Data.SqlClient --version 4.3.1

However, the package is installed as following,

<PackageReference Include="System.Data.SqlClient" Version="4.3.1" />

And currently NOT SUPPORT installing a tool reference. For example, what we expected for adding a tool package,

<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0-preview1-final" />

Follow nuget issue #4190 for a update in the near future.



Remove package

$ dotnet remove package <package name>




Reference


Add reference

$ dotnet add reference <related path>\<project name>.csproj

For example,
dotnet add reference ..\JB.Sample.Core.csproj


Remove reference

$ dotnet remove reference <related path>\<project name>.csproj



List all reference

$ dotnet list reference






dotnet tool




dotnet-ef

Install

$ dotnet tool install --global dotnet-ef


Upgrade

$ dotnet tool update --global dotnet-ef


See version

$ dotnet tool --version




Reference




[Scrum] Scrum Alliance 調查報告 (2016)


1.      如果你想要在全球市場保持競爭力,你將不得不採納某種形式的敏捷性。

2.      Scrum已被應用在除了IT及軟體開發的其他行業。
調查結果顯示,大多數受訪者表示在IT或軟件開發的其他部門也在使用它。
實際上,21%的Scrum項目由IT外部的部門經營。

3.      ScrumMaster正在演變成共同的角色。
ScrumMaster
一個非常棒的角色;這個人是組織變革的專家,可以與VP交談,與團隊交流,消除障礙等等。 雖然ScrumMaster大多來自技術背景,由於許多因素,角色正在轉移給項目經理或其他管理人員擔任。

4.      Scrum仍是一個流行的框架,約三分之一的調查受訪者表示只使用它。 有趣的是,近三分之二使用它與其他敏捷方法。

[Agile] 透過Pair programming 學習並加速專案開發

Pair Programming XP 的一種方法和實踐。




五月開始,小弟又接到了一個支援其他專案開發的任務; 因為這個案子趕在六月要上線,五月該團隊只留下一位剛接觸ASP.NET MVC不久的同事。

不過我自己也有專案在身,無法同時兼顧談需求和開發的角色,因此我大膽的提議,請這位同事先專心在理解需求和開Spec,我自己先去熟悉一下程式。

過了一個禮拜,Spec開出來了,我也大致了解了程式的架構,接下來就是開發了; 

我希望未來同事也應該須具備開發這套系統的能力, 如果我寫完再跟他說明,似乎不是一個很有效率的方式,因此決定採用Pair Programming。

一開始的時候都是我邊打Code邊向他說明為何我這麼寫, 同時熟悉邏輯和需求的同事也可以馬上驗證我的輸出是正確的; 因此很快地就完成了第一個功能。

接下來相對簡單的代碼,換他開始動手寫,然後我們一起驗證Code是正確的嗎? 結果也是正確的嗎? 

我們兩個人在位子上或在會議室一起Pair Programming不到三周後, 原本預計20工作天的工作已經完成了,可以提早UAT了;

而且有一些功能是我同事後來趁我忙其他事的時候做掉了; 

所以我們專案開發不但如期交付,在過程中,我們也對原本不熟悉的程式碼漸漸掌握了。 

而且, 下次我應該就可以不用支援了 (這才是重點阿~~~!)