Tuesday, March 24, 2020

.NET Core React Projesinin Docker ile Linux Üzerinde Çalıştırılması - Running .NET Core React Project on Linux with Docker

Bildiğimiz üzere .NET Core projelerinin en büyük avantajlarından biri her türlü işletim sisteminde çalıştırılabilmesidir. Bunların yanında "Dependency Injection" vb. bir çok mimari geliştirmesi de geliştiricilere .NET Core ile sunulmaktadır. React ise front-end olarak kullanılan ve Babel derleyicisi (compiler) ile derlenen ve bir front-end teknolojisidir. React uygulamasını derlemek için NodeJs kullanmamız gerekir. JSX ise basitçe javascript içine HTML kodları yazmamızı sağlayan bir javascript uzantısıdır. Docker ise bir işletim sistemi üzerinde birbirinden izole, mikro servis mimarisine destek veren, her bir uygulamanın kendi çalışma ortamına sahip olduğu konteynerler  oluşturmamızı sağlayan teknolojidir. Bu yazıda konumuz bunlar olmadığı için bunlardan derinlemesine bahsedilmeyecektir.

Burada anlatacağımız örnek React için olsa da  Dockerfile içinde ilgili kısımlar güncellenerek diğer front-end teknojileri içinde çalıştırılabilir. Dockerfile konteynerlerimizi nasıl oluşturacağımızı belirttiğimiz dosyadır. Docker file Visual Studio (VS) üzerinden projenin üzerine sağ tıklanarak açılan menüden Add->Docker Support üzerinden oluşturulabilir.


Fakat buradan oluşturduğumuz Dockerfile içinde NodeJs kurumu içermediği için çalışmayacaktır. Bu sebeple imajlar üzerinde işlem yaparken aşağıdaki kod bloğu ile NodeJs kurulumlarının da yapılması gerekmektedir:

RUN apt-get update -yq \
    && apt-get install curl gnupg -yq \
    && curl -sL https://deb.nodesource.com/setup_10.x | bash \
    && apt-get install nodejs -yq

Yukarıda kod bloğu VS üzerinden oluşturduğumuz Dockerfile üzerine eklendiğinde dosyamızın son hali aşağıdaki gibi olacaktır:

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base

RUN apt-get update -yq \
    && apt-get install curl gnupg -yq \
    && curl -sL https://deb.nodesource.com/setup_10.x | bash \
    && apt-get install nodejs -yq

WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM microsoft/dotnet:2.1-sdk AS build

RUN apt-get update -yq \
    && apt-get install curl gnupg -yq \
    && curl -sL https://deb.nodesource.com/setup_10.x | bash \
    && apt-get install nodejs -yq

WORKDIR /src
COPY ["TuringApp/TuringApp.csproj", "TuringApp/"]
RUN dotnet restore "TuringApp/TuringApp.csproj"
COPY . .
WORKDIR "/src/TuringApp"
RUN dotnet build "TuringApp.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "TuringApp.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .

ENTRYPOINT ["dotnet", "TuringApp.dll"]

Dockerfile hazır olduğuna göre sıra projemizi çalıştırmaya geldi. Bunun için proje kodlarımızı (kaynak kodlarını) derleme yapacağımız Linux üzerindeki bir klasöre kopyalıyoruz. Proje klasörümüzün hemen yanına da Dockerfile kopyalıyoruz. Daha açıklayıcı olması bakımından dosyaları kopyadıktan sonra klasör şöyle görünmelidir:


Bu aşamadan sonra Dockerfile kullanarak imaj oluşturmamız gerekiyor. Linux terminali açıyoruz ve ilk olarak aşağıdaki kodu çalıştırıyoruz:

sudo docker build -t firstcore .

Burada "firstcore" yeni oluşturacak imaja verdiğim isimdir. Siz kendinize göre isimlendirebilirsiniz.


İmajımız başarılı bir şekilde oluşturulursa terminalin son hali aşağıdaki gibi olacaktır:


Ayrıca aşağıdaki komutu kullanarak imajımızın docker tarafından listelendiğini görebiliriz:

sudo docker image ls


Bu aşamadan sonra sıra imajımızı çalıştırıp projemizi host etmeye geldi. Bunun için terminalde aşağıdaki komutu çalıştırıyoruz:

sudo docker run --name firstproj -p 8080:80 firstcore:latest


Run komutunda yer alan -p 8080:80 komutuyla 8080 portuna gelen istekleri uygulamamızın dinlediği 80 portuna yönlendiriyoruz. Name komutuyla konteyner ismini firstproj olarak verdik. Uygulamanın hangi portu dinlediği docker run komutunu çalıştırdıktan sonra teminalde görünmektedir. Bu aşamadan sonra tarayıcı üzerinden localhost:8080 üzerine istek yapıldığında projemiz tarayıcıda çalıştırılacaktır.



İşletim Sistemi:

Ubuntu 18.04.4 LST (Bionic Beaver)

Proje (Turing):

https://github.com/vyigity/TuringApp

Veri tabanı:

MySQL

Thursday, December 13, 2018

ProjectBase ile Transaction Üzerinden Dış Veri Erişim Yapıları ile Entegrasyon (Entity Framework 6 Örneği)

ProjectBase (PB) kütüphanesi veri erişim mantalitesi olarak ORM (Object Relational Mapping) ile provider seviyesi arasında bir konumda bulunmaktadır. PB dış bir veri erişim yapısı ile beraber kullanılmak istendiğinde ortak transaction kullanım gerekliliği doğabilmektedir. Örneğin ORM olarak Entity Framework 6 (EF) kullanıyoruz ve bazı işlemleri EF ile bazılarını PB ile yapacağız fakat bu işlemlerin aynı transaction üzerinde yapılması gerekmekte. Bu durumda EF üzerinde aşağıdaki kod ile bir transaction başlatabiliriz:

DataBaseContext context = new DataBaseContext();
DbTransaction transaction =  context.Database.BeginTransaction().UnderlyingTransaction

Burada elde ettiğimiz transaction nesnesi veri tabanı bağlantısı açık ve işlem yapmaya hazır olan bir nesnedir. Bu işlemden sonra bu transaction nesnesini PB de kullanabiliriz:

IDatabase2 db = DatabaseFactory.GetDbObject(DbSettings.TransactionMode);
db.UseExternalTransaction(transaction);

Buradan sonra PB ile yaptığımız işlemler EF ile oluşturduğumuz transaction üzerinden yapılacaktır. Normal EF kullanımından farklı olarak EF 6'da transaction bu yazıda bahsettiğimiz yöntemle başlatıldığında Commit edilmesi gerekmektedir:

context.Save();
context.Database.CurrentTransaction.Commit();

Bu işlem sırasında aşağıdaki hususlara dikkat edilmesi gerekir:

1. Dışarıda oluşturulan transaction geçerli bir durumda olmalı ve veri tabanı bağlantısı açık olmalıdır.
2. PB ile Commit veya RollBack yapıldığında PB sıralı yeni işlemleri yeni transaction kullanarak yapacaktır. Yani mevcut transaction geçerliliğini yitirecektir.
3. Bu işlem yapılırken PB transaction modda çalışır durumda olmalıdır.

Sunday, March 18, 2018

ProjectBase (PB) ile Asenkron Programlama - Asynchronous Programming with ProjectBase (PB)

PB v4.0.0-beta ile veri tabanı işlemlerinin asenkron olarak yapılabilmesi amacıyla PB içine asenkron mimari eklendi. Asenkron programlama (AP) .NET 4.5 ile ortaya çıktığı için PB'nin kullanılması için minumum .NET 4.5 kullanılması gerekmektedir. AP'de yapılacak yan işler ana işten ayrılarak asenkron bir şekilde halledilmekte ve ana işin bu yan işler sebebiyle meşgul edilmesi engellenebilmektedir. AP bir çok amaçla kullanılabilmekle beraber Forms uygulamalarında UI (User Interface) thread, yani arayüz elemanlarını oluşturan thread, genellikle ana thread olmakla beraber tamamlanması uzun işlerde bu thread uzun süre meşgul edilirse arayüzün cevap vermemesine neden olmaktadır. Ayrıca form yapısında bir arayüz elemanını oluşturan thread'den başka bir thread bu elemana ulaşmaya kalkıştığında ön tanımlı (default) olarak uygulama hata verecektir. AP form uygulamalarında kullanıldığında UI thread sürekli meşgul edilmediği için arayüz sürekli aktif kalacaktır.

AP ayrıca işi birden fazla Thread olarak yaptığı için bu işlerin işlemcilere dağıtımı kolay olmakta ve işlemci sayısına göre hız kazanımı elde edilebilmektedir.

Bu amaçla bu yazıda PB v4.0.0-beta kullanan bir örnek form uygulaması hazırlandı. Burada PB'nin kodlarını Github üzerinde indirip ilgili fonksiyonları Thread.Sleep() fonksiyonu kullanıp geciktirerek AP'nin etkisini daha rahat gözlemleyebilirsiniz. Oluşturulan forms arayüzü aşağıda verilmiştir:



Arayüzde 2 tane GridView, bir button ve bir label kullanılmıştır. DOLDUR tuşuna basıldığında label üzerine yazı yazılmadan önce veri çekme işlemleri başlatılmakta, yazı yazdırılmakta ve veriler GridView'lar içine yüklenmektedir. GridView'lar içine veriler kendi aralarında da eş zamansız yüklendiği için aynı anda yüklenmemektedir. Bu işlemler esnasında arayüz sürekli cevap verir durumda olacaktır.

PB v4.0.0-beta bağlantı yönetimini (Connection Management) eş zamansız (asenkron) yapmamaktadır. Yani bağlantı işlemleri gerçekleştirmek için işlemleri yürüten ana thread kullanılmaktadır. Bu yüzden bağlantı sırasında kısa süreli bir meşguliyet meydana gelebilmektedir. Aşağıda forms uygulamasının kodları verilmiştir:


public class EMPLOYEE
{
    public string FIRST_NAME { get; set; }
    public string LAST_NAME { get; set; }
    public decimal SALARY { get; set; }
    public DateTime? HIRE_DATE { get; set; }

}

private async void BtIslem_Click(object sender, EventArgs e)
{
    string sql = "select * from employees";
    string sql2 = "update employees set first_name = 'VELI YIGIT' where employee_id = 101";

    var db = DatabaseFactory.GetDbObjectAsync(DbSettings.ManuelConnectionManagement);

    var task = db.GetObjectListAsync(sql);
    var task2 = db.ExecuteQueryAsync(sql2);
    var task3 = db.GetObjectListAsync(sql);

    LbBilgi.Text = "Verilerin gelmesini beklememe gerek yok!";

    GwData.DataSource = await task;
    await task2;
    GwData2.DataSource = await task3;

    db.CloseConnection();
}

Tuşun basılma (click) olayında (event) "async" anahtar kelimesi ile asenkron kod içerdiği belirtilmektedir. Asenkron işlemlerin sonuçları "await" anahtar kelimesi kullanılarak beklenmektedir. Bir noktada await kullanıldı ise ana thread beklenen işlem sonuçlanana kadar alt satırları işlememektedir. Yukarıdaki kod bloğunda görüldüğü üzere 2 veri çekme işlemi ve bir güncelleme (update) işlemi asenkron olarak başlatılmaktadır. Label üzerine yazı yazdırıldıktan sonra (bu arada veritabanı işlemleri asenkron olarak devam etmekte), daha sonra sonuçlar beklenerek ilgili arayüz elemanları güncellenmektedir. Bu bekleme işlemi sırasında ana thread bloklanmadığı için arayüz sürekli cevap verir durumda olacaktır.

Burada bir diğer önemli nokta "Manuel Connection Management" modunun kullanılmasıdır. Otomatik yönetimde yan thread bağlantıyı diğerleri işlemler sonuçlanmadan kesebilmekte ve bu durum işlemlerin hatalı sonuçlanmasına neden olabilmektedir. Bu sebeple AP kullanılacak ise Manuel mod veya Transaction mod kullanılması gerekmektedir.

Bütün veritabanı işlemleri sonuçlandıktan sonra bağlandı kesilmektedir.

"Bu yazıda "Oracle Managed Provider", Oracle 11g Express Edition ve HR şeması test verileri kullanılmıştır."

 "https://github.com/vyigity/ProjectBase"


Saturday, December 16, 2017

ProjectBase kütüphanesi ile evrensel (global) parametre kullanımı

ProjectBase (PB) v2.5.0 ile birçok QueryGenerator (QG) sıkıntısı çözüldü ve global parametre özelliği kütüphaneye eklendi. Eski usülde parametre kullanımları eğer veri tabanı bağımsız tanımlanmak isteniyorsa FilterText özelliği içinde verilmeliydi. Bu yöntemde eğer parametre karakteri veri tabanı ile uyumsuz ise değiştiriliyordu. Bu yöntem lokal parametre kullanımı olarak tanımlanmaktadır. Global parametre kullanımında parametre SelectText, FilterText veya SelectTail içinde kullanılabilmektedir. Bu özellik ayrıca karmaşık sql komutlarının parametrik olarak QG tarafından işlenebilmesine de olanak sağlamaktadır. Aşağıdaki örneği inceleyelim:

var db = DatabaseFactory.GetDbObject();
var gen = QueryGeneratorFactory.GetDbObject(ParameterMode.Global);

string sql = @"
               select * from employees e
                            
               where e.employee_id =
 
               (select max(employee_id) from employees 
                where salary > .p.SALARY and commission_pct > .p.COMMISSION)
              ";

gen.SelectText = sql;
gen.AddFilterParameter("SALARY", 8000);
gen.AddFilterParameter("COMMISSION", 0.1d);

var dt = db.ExecuteQueryDataTable(gen.GetSelectCommandBasic());

Burada karmaşıklığı artırmak için iç içe select cümlecikleri kullandık ve iç cümleciğe parametrik olarak bir değer gönderdik. Global parametre tanımında parametre tanımlaması ".p." prefiksi kullanılarak yapılmaktadır. Örnekte gösterildiği gibi "SALARY" isimli parametre ".p.SALARY" olarak tanımlanmıştır. Neden ".p." gibi bir şey prefiks olarak tercih edildi diye soracak arkadaşlar için burada karmaşık bir derleyiciye sahip olmadığımız için örüntü (pattern) olarak yazılabilecek sql cümlecikleri içinde en az denk gelebilecek bir yapıyı tercih etmemiz gerekiyordu diye soruyu cevaplayabilirim. Yukarıdaki kod çalıştırıldığında sonuç DataTable olarak elde edilecektir.

"Bu yazıda "Oracle Managed Provider", Oracle 11g Express Edition ve HR şeması test verileri kullanılmıştır."

 "https://github.com/vyigity/ProjectBase"

Saturday, November 11, 2017

FluentGenerator kullanarak Oracle Entity Framework 6.x sınıflarının oluşturulması - Generating Oracle Entity Framework classes with FluentGenerator

FluentGenerator (FG) Entity Framework 6.x (EF) code first örüntüsüne göre sınıfların oluşturulması için yazılmış bir masaüstü uygulamasıdır. Oluşturulan sınıflar "fluent" mimariye göre oluşturulur ve POCO ve bağlantı (mapping) sınıfları ayrı ayrı oluşturulur.

FG, ProjectBase (PB) kütüphanesini kullandığı için uygulamanın çalıştırılması için PB' nin eklenmesi ve provider ayarlamalarının yapılması gerekir. Eğer Oracle unmanaged provider kullanılıyorsa "Oracle Client" kurulmalı ve ilgili ayarlamalar yapılmalıdır.

FG ve PB'yi aşağıdaki adresten indirebilirsiniz:

https://github.com/vyigity

Test veri tabanı olarak Oracle 11g express edition ve HR şeması içinde yer alan ögeler kullanılacaktır.



FG çalıştırıldığında config ayarlamalarına göre bağlantı cümleciği arayüzde görünecektir. Cümlecik değiştirilmek istenirse istenen yeni cümlecik girilir ve "CHANGE" tuşu ile yeni cümlecik geçerli hale getirilir. Namespace ismini kendi projenize göre belirledikten sonra "DIR" tuşu ile EF sınıflarının hangi klasör içinde oluşturulacağı seçilmelidir. Capitalization kısmında sınıf isimlerinin nasıl formatlanacağı seçilebilmektedir. Bu örnekte "Pascal" kullanılacaktır. Üstte yer alan TABLE_NAME kısmında tablo ismine göre, alttaki VIEW_NAME kısmından view ismine göre arama yapılabilir.

Örnek olarak görselde de görüleceği üzere "Employees" tablosuna ilişkin sınıfları oluşturmak istiyoruz. Arama kısmına "EMPL" yazarak istediğimiz tabloya ulaştık ve yön tuşları ile tabloyu sağ tarafta yer alan grid içine alarak bu tablonun oluşturulacağını belirttik. Son olarak "GENERATE" tuşu nu kullandığımızda önceden seçtiğimiz klasör içinde sınıflar aşağıdaki gibi oluşturulacaktır:

.\Generate\Context.cs

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using YourNameSpace.Models.Mapping;

namespace YourNameSpace.Models
{
    public partial class DatabaseContext : DbContext
    {
        static DatabaseContext()
        {
            Database.SetInitializer<DatabaseContext>(null);
        }

        public DatabaseContext()
            : base("Name=Context")
        {
        }

        public DbSet<Employees> Employeess { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new EmployeesMap());
        }
    }
}

Context sınıfı EF'nin temel sınıfıdır ve FG foreign key (FK) ilişkileri oluşturmadığı için bu konuyla ilgili kodlar elden bu sınıf içine eklenmelidir. Map sınıfları "OnModelCreating" fonksiyonu içine yukarıdaki gibi eklenmeli ve DbSet tanımlaması sonraki eklentiler için elden yapılmalıdır.

.\Generate\Employees.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace YourNameSpace.Models
{
    public partial class Employees
    {
        public int EmployeeId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string PhoneNumber { get; set; }
        public DateTime HireDate { get; set; }
        public string JobId { get; set; }
        public Nullable<decimal> Salary { get; set; }
        public Nullable<decimal> CommissionPct { get; set; }
        public Nullable<int> ManagerId { get; set; }
        public Nullable<short> DepartmentId { get; set; }

    }
}

Yukarıda EF Employees POCO sınıfının nasıl göründüğü verilmiştir.

.\Generate\Mapping\EmployeesMap.cs

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;

namespace YourNameSpace.Models.Mapping
{
    public class EmployeesMap : EntityTypeConfiguration<Employees>
    {
        public EmployeesMap()
        {
            // Primary Key
            this.HasKey(t => new { t.EmployeeId });

            // Properties
            this.Property(t => t.EmployeeId)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

            this.Property(t => t.FirstName)
                .HasMaxLength(20);

            this.Property(t => t.LastName)
                .IsRequired()
                .HasMaxLength(25);

            this.Property(t => t.Email)
                .IsRequired()
                .HasMaxLength(25);

            this.Property(t => t.PhoneNumber)
                .HasMaxLength(20);

            this.Property(t => t.JobId)
                .IsRequired()
                .HasMaxLength(10);

            // Table & Column Mappings
            this.ToTable("EMPLOYEES", "HR");
            this.Property(t => t.EmployeeId).HasColumnName("EMPLOYEE_ID");
            this.Property(t => t.FirstName).HasColumnName("FIRST_NAME");
            this.Property(t => t.LastName).HasColumnName("LAST_NAME");
            this.Property(t => t.Email).HasColumnName("EMAIL");
            this.Property(t => t.PhoneNumber).HasColumnName("PHONE_NUMBER");
            this.Property(t => t.HireDate).HasColumnName("HIRE_DATE");
            this.Property(t => t.JobId).HasColumnName("JOB_ID");
            this.Property(t => t.Salary).HasColumnName("SALARY");
            this.Property(t => t.CommissionPct).HasColumnName("COMMISSION_PCT");
            this.Property(t => t.ManagerId).HasColumnName("MANAGER_ID");
            this.Property(t => t.DepartmentId).HasColumnName("DEPARTMENT_ID");
        }
    }
}

EmployeesMap sınıfı EF haritalama ve bazı validation kodlarını içeren sınıftır. Validation kodlarının burada bulunması zorunlu değildir. İstenmesi durumunda etiket (Attribute) usulü de uygulanabilir. Bütün sınıfları projenize eklediğinizde EF ile ilgili veri tabanı nesneleri üzerinde işlem yapmaya başlayabilirsiniz.

Monday, October 16, 2017

ProjectBase ile DataTable Yerine Önceden Tanımlı Nesne Kullanarak Veri Çekme (Tip Belirtimli - Typed)

Daha önceki yazılarda DataTable üzerinde veri çekme örneği yapmıştık. DataTable ile yapılan işlemler "Typed" yani belirli türlü olmadığı için kodlamayı zorlaştırmaktadır. Şimdi kendi oluşturduğumuz bir sınıfa ait nesneyi verileri tutmak için kullanacağız. Yine "EMPLOYEES" tablosunu kullanalım ve önce sınıfı aşağıdaki gibi oluşturalım:

public class EMPLOYEE
{
    public string FIRST_NAME { get; set; }
    public string LAST_NAME { get; set; }
    public decimal SALARY { get; set; }
    public DateTime? HIRE_DATE { get; set; }

}

Burada dikkat edilmesi gereken husus yazacağımız sorgudan gelecek olan sütun isimlerinin "Property" isimleri ile eşleşmesidir. Eşleme büyük-küçük harfe duyarlı değildir. Şimdi bu sınıfı kullanacak veri çekme kodlarını yazalım:

var db = DatabaseFactory.GetDbObject();
var gen = QueryGeneratorFactory.GetDbObject();

gen.SelectText = "select * from employees where employee_id < 112";

List<EMPLOYEE> employees = db.GetObjectList<EMPLOYEE>(gen.GetSelectCommandBasic());

Yukarıdaki kod çalıştırıldığında id numarası 112'den küçük kayıtları yazdığımız sınıfın bir listesi olarak elde edeceğiz.

"Bu yazıda "Oracle Managed Provider", Oracle 11g Express Edition ve HR şeması test verileri kullanılmıştır."

"https://github.com/vyigity/ProjectBase"

Sunday, October 15, 2017

ProjectBase Data Access and Utility Library - ProjectBase Veri Erişim ve Yazılım Destek Kütüphanesi

ProjectBase (PB) is a Utility and Data Access library. PB designed with a database independent interface oriented approach to ensure extentibility and reliablity. PB's codes can be changed or manipulated easily. PB can be installed with nuget package manager console:
PM> Install-Package vyigity.ProjectBase -Version 4.0.0

PB has 3 type of data access:
  • Automatic Connection Management: Using single connection object and while executing a command, connection is opened and closed automatically.
  • Manuel Connection Management: Using single connection object and connection is opened and closed by developer manually.
  • Transaction Mode: PB supports PL/SQL type code writing of transactional processes. PB creates transactions and manages them automatically.
PB currently supports Oracle (Managed-Unmanaged Provider), SQL Server databases and OleDb. PB supports MySql provider with v2.0.0 and PostgreSQL provider with v3.0.0.
Also PB supports low level object mapping features.

Project Adress:
For introduction:
For connection management examples:
For transactions examples:
For DML examples:
For parametric database procedures and functions examples: