[netCore]SQL Server on Linux and netCore2.0 開發初體驗

微軟愛Linux有一段時間了,SQL Server on Linux這目標也看到光了,

隨者.Net Core 2.0 Announcing,今天我就透過GDD和SOD方式動手實作

(我沒很了解.netCore,但這兩種方式對我來說有快速學習的效果~哈),

看看.Net Core 2.0是否讓我有不同的開發體驗。

Runing a SQL Server of Linux in a Docker

我透過docker快速建立我的開發環境

docker search mssql

可以看到 microsoft/mssql-server-linux 高達377顆星,

所以我等等會使用該docker image來建立我第一個SQL Server of Linux。

 

docker pull microsoft/mssql-server-linux

使用該image前,請注意一下必要條件如下

docker run  -e “ACCEPT_EULA=Y” -e “SA_PASSWORD=XXXXXXX” -ti -p 1533:1433 microsoft/mssql-server-linux

完成必要條件後,就可在Linux順利啟動SQL Server。

 

使用SSMS連接SQL Server of Linux

該image所包的是SQL Server 2017(RC2) 14.0.900.75版本。

下面我將建立一個測試資料庫、資料表和SP,方便後面我使用 Dapper的.net core版本進行資料存取。

Show current data file path

可以看到目前系統資料庫檔案都存放在/var/opt/mssql/data,但使用者資料庫我想存放在/opt/mssql/data

#Use the docker exec -it command to start an interactive bash shell inside your running container
docker exec -it 858775108f6e "bash"

note:

docker ps –a –show container

docker rm  <CONTAINER ID> --remove container

docker start <CONTAINER ID> --start container

docker stop <CONTAINER ID> --stop container

#Display files of location /var/opt/mssql/data
ls -lrt /var/opt/mssql/data

#create a directory named data under /opt/mssql/
mkdir /opt/mssql/data
ls -lrt /opt/mssql/data

--Create database via ssms
create database ricolinux on primary
(
name='ricolinux',filename='/opt/mssql/data/ricolinux.mdf',size=100mb,maxsize=unlimited,filegrowth=10mb
)
log on
(
name='ricolinux_Log',filename='/opt/mssql/data/ricolinux_Log.ldf',size=100mb,maxsize=unlimited,filegrowth=10mb
)

 

Check ricolinux database file path

相關檔案已經放置我想要的路徑了。

use ricolinux
go
create table Employee
(
serial int not null
,cname nvarchar(30) not null
,ename varchar(30) not null
,birthday date not null
,storeon datetime not null default(getdate())
)
go
create proc usp_GetEmployee(@serial int)
as
set nocount on
select * from dbo.Employee where serial=@serial

 

Use .Net core to access SQL Server of Linux

看了官方文件,需要安裝Microsoft.Extensions.Configuration和Microsoft.Extensions.Configuration.Json,

安裝完後我們可以透過編輯*.csproj再次確認相依檔案。

appsettings.json

另外我將使用Dapper來存取SQL Server of Linux

class Employee
    {
        public int Serial { get; set; }
        public string Cname { get; set; }
        public string Ename { get; set; }
        public DateTime Birthday { get; set; }
        public DateTime Storeon { get; set; }
    }

 internal class BaseRepository
    {
        public string ConnectionString {
            get
            {
                //read appsettings.json
                var builder = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
                //create IConfigurationRoot object
                var Configuration = builder.Build();
                return Configuration["ConnectionStrings:linuxsql"];
            }
        }

        public IDbConnection Connection=> new SqlConnection(ConnectionString);
    }
    
    internal sealed class EmployeeRepository: BaseRepository
    {
        private readonly string _connectionString;
        private readonly IDbConnection _connection;
        public EmployeeRepository()
        {
            _connectionString = base.ConnectionString;
            _connection = base.Connection;
        }

        public IEnumerable<Employee> GetAll()
        {
            using (_connection)
            {
                _connection.Open();
                return _connection.Query<Employee>("SELECT * FROM dbo.Employee");
            }
        }

        public IEnumerable<Employee> GetEmployeesWithSP(int serial)
        {
            using (_connection)
            {
                _connection.Open();
                return _connection.Query<Employee>("usp_GetEmployee", new { serial = serial },
                    commandType: CommandType.StoredProcedure);
            }
        }

        public int Add(Employee prod)
        {
            using (_connection)
            {
                string sQuery = @"INSERT INTO dbo.Employee (Serial, Cname, Ename, Birthday, Storeon)
                                  VALUES(@Serial, @Cname, @Ename, @Birthday, getdate())";
                _connection.Open();
                return _connection.Execute(sQuery, prod);
            }
        }

        public int Delete(int serial)
        {
            using (_connection)
            {
                string sQuery = @"DELETE FROM dbo.Employee WHERE serial = @serial";
                _connection.Open();
                return _connection.Execute(sQuery, new { serial = serial });
            }
        }

        public int Update(Employee employee)
        {
            using (_connection)
            {
                try
                {
                    string sQuery = @"UPDATE dbo.Employee SET Cname = @Cname,
                                Ename = @Ename, Birthday= @Birthday, Storeon=getdate()
                                WHERE Serial = @Serial";
                    _connection.Open();
                    return _connection.Execute(sQuery, employee);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    return 0;
                }
            }
        }

    }

Main Entry

class Program
    {
        private static EmployeeRepository _employeeRepository;
        static void Main(string[] args)
        {
            Console.WriteLine("\nWhat is your statement? ");
            var sqlLine = Console.ReadLine();
            string[] inputValues;
            int result = 0;
            _employeeRepository =new EmployeeRepository();
            switch (sqlLine.ToUpper().Substring(0, Math.Min(3, sqlLine.Length)))
            {
                case "GET":
                    _employeeRepository.GetAll().ToList().ForEach(
                        s => Console.WriteLine($"Serial:{s.Serial}, Cname:{s.Cname}, Ename:{s.Ename}, Birthday:{s.Birthday}, Storeon:{s.Storeon}")
                        );
                    break;
                case "INS":
                    inputValues = sqlLine.Substring(6).Split(',');
                    var employeeadd = new Employee()
                    {
                        Serial = int.Parse(inputValues[0]),
                        Cname= inputValues[1],
                        Ename= inputValues[2],
                        Birthday =DateTime.Parse(inputValues[3])
                    };
                    result= _employeeRepository.Add(employeeadd);
                    Console.WriteLine( (result > 0) ? "successed": "failed");
                    break;
                case "DEL":
                    inputValues = sqlLine.Substring(6).Split(',');
                    result = _employeeRepository.Delete(int.Parse(inputValues[0]));
                    Console.WriteLine((result > 0) ? "successed" : "failed");
                    break;
                case "UPD":
                    inputValues = sqlLine.Substring(6).Split(',');
                    var employeeupd = new Employee()
                    {
                        Serial = int.Parse(inputValues[0]),
                        Cname = inputValues[1],
                        Ename = inputValues[2],
                        Birthday = DateTime.Parse(inputValues[3])
                    };
                    result = _employeeRepository.Update(employeeupd);
                    Console.WriteLine((result > 0) ? "successed" : "failed");
                    break;
                case "EXE":
                    inputValues = sqlLine.Substring(6).Split(',');
                    _employeeRepository.GetEmployeesWithSP(int.Parse(inputValues[0])).ToList().ForEach(
                        s => Console.WriteLine($"Serial:{s.Serial}, Cname:{s.Cname}, Ename:{s.Ename}, Birthday:{s.Birthday}, Storeon:{s.Storeon}")
                    );
                    break;
                default:
                    break;
            }
            Console.Write("\nPress any key to exit...");
            Console.ReadKey(true);
        }
    }

 

Install .net core 2.0 on linux

apt-get install curl
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
cat /etc/issue #determine Ubuntu version number
sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main" > /etc/apt/sources.list.d/dotnetdev.list'
apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893
apt-get update
apt-get upgrade
apt-get install dotnet-sdk-2.0.0

#Confirm you have the correct version installed
dotnet --version

Copy myapp files to linux container

mkdir docker-netcoreapp

#copy all of .net core files  to /docker-netcoreapp/
docker cp D:\riconetcore\AccessLinuxSQL\. 858775108f6e:/docker-netcoreapp/. 

note:我設定local的D分享給container

Start myapp on Ubuntu

Cd docker-netcoreapp
dotnet AccessSQLofLinux.dll

insert

Getall

Update

Execsp

透過呼叫SP確認剛剛資料已被正確更新。

 

Delete

stop container

docker commit 858775108f6e microsoft/mssql-server-linux
docker stop 858775108f6e

 

感想

我寫這隻簡單.netCore app存取 linux sql server,花較多時間在Liunx command,

至於安裝SQL Server on Linux,透過docker可說輕鬆省事毫不費力,

寫code過程差異較大就是組態設定檔案的存取,以及檔案相依性問題排除,

後面有空再來玩玩web api。

 

參考

Quickstart: Run the SQL Server 2017 container image with Docker

Build a C# Hello World application with .NET Core in Visual Studio 2017

.NET Core Data Access

microsoft/mssql-server-linux

How to Change Default Data and Log file directory for SQL Server running on Linux

Run the SQL Server 2017 container image with Docker

.NET Core 2.0 Changes – 4 Key Things to Know

project.json 與 csproj 屬性的對應

Configuration in ASP.NET Core

https://blogs.msdn.microsoft.com/fkaduk/2017/02/22/using-strongly-typed-configuration-in-net-core-console-app/

https://docs.docker.com/engine/reference/commandline/save/

Download .NET Core for Linux

Install .NET and build your first app on Ubuntu or Mint