İş süreçlerini kontrol edebileceğimiz Business katmanını oluşturuyoruz.
Model Katmanının Oluşturulması
Verilerimizi transfer edeceğimiz Model katmanını oluşturuyoruz.
Referans Projesi olarak aşağıdaki projeyi ekliyoruz.
Repository Katmanının Oluşturulması
Veritabanı işlemlerini yönetebildiğimiz data katmanımız olan Repository katmanını oluşturuyoruz.
Repository katmanına aşağıdaki referansları ekleyin.
Referans projesini olarak aşağıdaki kütüphaneleri ekliyoruz.
Common Layer
Extension Dosyalarının Oluşturulması
Array değişkenlerimize yardımcı olması için aşağıdaki extension'ı yazıyoruz.
Primitive değişkenlerimize yardımcı olmak için aşağıdaki extension'ı yazıyoruz.
Common Model Dosyalarının Oluşturulması
Kullanıcılardan parametrik olarak alacağımız veriler için base request sınıfı oluşturuyoruz.
GraphQL için aşağıdaki model dosyamızı oluşturuyoruz.
Entity Katmanı
Company Entity
Product Entity
Model Katmanı
Search Company Database Transfer Object
Kullanıcıdan aldığımız verileri alt katmanlara aşağıdaki veri deseniyle göndereceğiz.
Repository Katmanı
IRepositoryBase Interface Oluşturulması
Repository sınıflarımız için generic base bir Interface tanımlıyoruz.
RepositoryBase Sınıfının Oluşturulması
Oluşturacağımız Entity Repository'leri için RepositoryBase sınıfını, IRepositoryBase interface'i üzerinden oluşturuyoruz.
ICompanyRepository Interface Oluşturulması
CompanyRepository Sınıfının Oluşturulması
Business Katmanı
Company Entity'sinin iş süreçlerini yazmak için ICompanyService adında bir interface oluşturuyoruz.
Şimdilik iki tane metod yazacağız.
ICompanyService Oluşturulması
CompanyService Sınıfının Oluşturulması
ICompanyService üzerinden CompanyService adında yeni bir sınıf oluşturuyoruz.
Web - Graphql Katmanı
GraphQL Schema adını verdiğimiz referans bir root sınıfından oluşur.
Schema sınıfına Resolver bir tane QUERY referans gösterilir..
Query dosyaları ise GraphType'lardan beslenerek dinamik sorgular oluşturacağımız bir yapı kurmamızı sağlar.
Kısacası Schema > Query > GraphTypes olarak tanımlayabiliriz.
Retail adında bir tane Root Schema sınıfımızı aşağıdaki gibi tanımlıyoruz.
RetailSchema sınıfının TutorialQuery adında bir sorgulama sınıfını çözümlediğini bildiriyoruz.
Schema Sınıfının Oluşturulması
Company Graph Type Sınıfının Oluşturulması
Entity sınıflarımızın field alanlarını kullanarak graphql için sorgulanabilir tipler oluşturuyoruz.
Aşağıdaki örneğimizde Company Entity dosyasımızı referans göstermiş ve field alanları için Type tanımlamalarını yapmış olduğumuzu göreceksiniz.
Örneğin Name alanı için ikinci parametre olan IsNullable alanını True vererek, bu field için null alabilen bir field düzenlemiş olduk. Diğer override parametrelerini kullanarak TYPE tipinide değiştirmeniz veya belirtmeniz mümkündür.
TutorialQuery Sınıfının Oluşturulması
Bu örneğimizde 2 tane sorgulama endpoint'i oluşturacağız.
Birinci endpoint; "company" adını verdiğimiz, CompanyGraphType kullanarak sorgu yapabileceğimiz,
companyId
companyName
isActive
parametrelerinden oluşan ve return type değeri COMPANY entity sınıfımızdır.
İkinci endpoint; "companies" adını verdiğimiz ve return tipi List<Company> olan sınıfımızdır.
Build & Run & Test
Create Database
Öncelikle yeni bir SQL SERVER veritabanı açmalısınız.
Sonrasında database klasörü içerisinde yer alan create-table.sql dosyasını çalıştırmalısınız.
Sizlerin projeyi test edebilmesi için; database klasörünün içerisinde yer alan bulk-data.sql dosyasını oluşturdum, bu dosyayı çalıştırabilirsiniz.
Projenin AppSettings dosyasını kendi connectionString değerlerinize göre güncellemesiniz.
DotNet Restore & Build & Run
Aşağıdaki komutları çalıştırıp projeyi ayağa kaldırabilirsiniz.
UI PlayGround
Tarayıcınızdan https://localhost:5001/ui/playground adresine gitmek istediğinizde; sorgularınızı test edebileceğiniz bir arayüze erişebilmeniz gerekmektedir.
Hello GraphQL For .Net
Aşağıdaki sorguyu yazdığınızda çıktısını almalısınız.
using System.Linq;
namespace CK.Tutorial.GraphQlApi.Common.Extension
{
public static class ArrayExtension
{
public static bool AnyItem(this string[] value)
{
return value != null && value.Any();
}
}
}
using System;
namespace CK.Tutorial.GraphQlApi.Common.Extension
{
public static class PrimitiveExtension
{
public static bool IsNotNullOrEmpty(this string value)
{
return !string.IsNullOrEmpty(value);
}
public static bool IsNotNull(this bool? value)
{
return value != null;
}
public static bool IsTrue(this bool? value)
{
return value != null && value == true;
}
public static byte ConvertToByte(this bool? value)
{
if (value ==null )
{
throw new ArgumentException("Boolean Item must be convertible");
}
return (byte) (value.Value ? 1 : 0);
}
}
}
namespace CK.Tutorial.GraphQlApi.Common.Model
{
public class RequestBase
{
public int DefaultPage { get; } = 1;
public int DefaultPageSize { get; } = 10;
public string[] Columns { get; set; }
public int? PageSize { get; set; }
public int? Page { get; set; }
}
}
using System.Text;
namespace CK.Tutorial.GraphQlApi.Common.Model
{
public class GraphQlQuery
{
public string OperationName { get; set; }
public string NamedQuery { get; set; }
public string Query { get; set; }
public string Variables { get; set; }
public override string ToString()
{
var builder = new StringBuilder();
builder.AppendLine();
if (!string.IsNullOrWhiteSpace(OperationName)) builder.AppendLine($"OperationName = {OperationName}");
if (!string.IsNullOrWhiteSpace(NamedQuery)) builder.AppendLine($"NamedQuery = {NamedQuery}");
if (!string.IsNullOrWhiteSpace(Query)) builder.AppendLine($"Query = {Query}");
if (!string.IsNullOrWhiteSpace(Variables)) builder.AppendLine($"Variables = {Variables}");
return builder.ToString();
}
}
}
namespace CK.Tutorial.GraphQlApi.Entity
{
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
}
}
using CK.Tutorial.GraphQlApi.Common.Enum;
namespace CK.Tutorial.GraphQlApi.Entity
{
public class Product
{
public int Id { get; set; }
public int CompanyId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int ProductTypeId { get; set; }
public Status Status { get; set; }
}
}
using CK.Tutorial.GraphQlApi.Common.Model;
namespace CK.Tutorial.GraphQlApi.Model.Request
{
public class SearchCompany : RequestBase
{
public int? Id { get; set; }
public string Name { get; set; }
public bool? IsActive { get; set; }
}
}
using SqlKata.Execution;
namespace CK.Tutorial.GraphQlApi.Repository
{
public class RepositoryBase<TEntity> : IRepositoryBase<TEntity>
{
public RepositoryBase(QueryFactory queryFactory)
{
QueryFactory = queryFactory;
}
public QueryFactory QueryFactory { get; set; }
}
}
ICompanyRepository.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using CK.Tutorial.GraphQlApi.Entity;
using CK.Tutorial.GraphQlApi.Model.Request;
using SqlKata.Execution;
namespace CK.Tutorial.GraphQlApi.Repository
{
public interface ICompanyRepository
{
Task<IEnumerable<Company>> GetCompanies(SearchCompany request);
Task<Company> GetCompany(SearchCompany request);
}
}
CompanyRepository.cs
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CK.Tutorial.GraphQlApi.Common.Extension;
using CK.Tutorial.GraphQlApi.Entity;
using CK.Tutorial.GraphQlApi.Model.Request;
using SqlKata.Execution;
namespace CK.Tutorial.GraphQlApi.Repository
{
public class CompanyRepository : RepositoryBase<Company>, ICompanyRepository
{
private readonly QueryFactory _queryFactory;
private const string TableName = "Company";
public CompanyRepository(QueryFactory queryFactory) : base(queryFactory)
{
_queryFactory = queryFactory;
}
public async Task<IEnumerable<Company>> GetCompanies(SearchCompany request)
{
var query = _queryFactory
.Query(TableName)
.When(request.Columns.AnyItem(),
q => q.Select(request.Columns))
.When(request.IsActive.IsNotNull(), q => q.Where("IsActive", request.IsActive.ConvertToByte()))
.OrderBy("Id")
.ForPage(request.Page ?? request.DefaultPage, request.PageSize ?? request.DefaultPageSize)
.GetAsync<Company>();
return await query.ConfigureAwait(false);
}
public async Task<Company> GetCompany(SearchCompany request)
{
var query = _queryFactory
.Query(TableName)
.When(request.Columns.AnyItem(),
q => q.Select(request.Columns))
.When(request.Id.HasValue, q => q.Where("Id", request.Id))
.When(request.Name.IsNotNullOrEmpty(), q => q.WhereContains("Name", request.Name))
.When(request.IsActive.IsNotNull(), q => q.Where("IsActive", request.IsActive.ConvertToByte()))
.OrderBy("Id")
.FirstOrDefaultAsync<Company>();
return await query.ConfigureAwait(false);
}
}
}
ICompanyService.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using CK.Tutorial.GraphQlApi.Entity;
using CK.Tutorial.GraphQlApi.Model.Request;
using SqlKata.Execution;
namespace CK.Tutorial.GraphQlApi.Business
{
public interface ICompanyService
{
Task<IEnumerable<Company>> GetCompanies(SearchCompany request);
Task<Company> GetCompany(SearchCompany request);
}
}
CompanyService.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using CK.Tutorial.GraphQlApi.Entity;
using CK.Tutorial.GraphQlApi.Model.Request;
using CK.Tutorial.GraphQlApi.Repository;
namespace CK.Tutorial.GraphQlApi.Business
{
public class CompanyService : ICompanyService
{
private readonly ICompanyRepository _companyRepository;
public CompanyService(ICompanyRepository companyRepository)
{
_companyRepository = companyRepository;
}
public Task<IEnumerable<Company>> GetCompanies(SearchCompany request)
{
return _companyRepository.GetCompanies(request);
}
public Task<Company> GetCompany(SearchCompany request)
{
return _companyRepository.GetCompany(request);
}
}
}
RetailSchema.cs
using CK.Tutorial.GraphQlApi.Web.Query;
using GraphQL;
namespace CK.Tutorial.GraphQlApi.Web.Schema
{
public class RetailSchema : GraphQL.Types.Schema
{
public RetailSchema(IDependencyResolver resolver) : base(resolver)
{
Query = resolver.Resolve<TutorialQuery>();
}
}
}
CompanyGraphType.cs
using System;
using CK.Tutorial.GraphQlApi.Business;
using CK.Tutorial.GraphQlApi.Entity;
using GraphQL.Types;
namespace CK.Tutorial.GraphQlApi.Web.Types
{
public class CompanyGraphType : ObjectGraphType<Company>
{
public CompanyGraphType()
{
Name = "Company";
Field(x => x.Id).Description("Şirket Benzersiz No");
Field(x => x.Name, true).Description("Şirket Adı");
Field(x => x.IsActive, true).Description("Şirket Aktif mi?");
}
}
}
TutorialQuery.cs
using System.Collections.Generic;
using CK.Tutorial.GraphQlApi.Business;
using CK.Tutorial.GraphQlApi.Model.Request;
using CK.Tutorial.GraphQlApi.Web.Extensions;
using CK.Tutorial.GraphQlApi.Web.Types;
using GraphQL.Types;
namespace CK.Tutorial.GraphQlApi.Web.Query
{
public class TutorialQuery : ObjectGraphType
{
public TutorialQuery(ICompanyService companyService)
{
#region Company
Field<CompanyGraphType>(
name: "company",
arguments: new QueryArguments(new List<QueryArgument>
{
new QueryArgument<IntGraphType>
{
Name = "companyId"
},
new QueryArgument<StringGraphType>
{
Name = "companyName"
},
new QueryArgument<BooleanGraphType>
{
Name = "isActive"
}
}),
resolve: delegate (ResolveFieldContext<object> context)
{
var columns = context.GetMainSelectedFields();
var companyId = context.GetArgument<int?>("companyId");
var companyName = context.GetArgument<string>("companyName");
var isActive = context.GetArgument<bool?>("isActive");
var request = new SearchCompany
{
Id = companyId,
Name = companyName,
IsActive = isActive,
Columns = columns
};
return companyService.GetCompany(request);
});
Field<ListGraphType<CompanyGraphType>>(
"companies",
arguments: new QueryArguments(new List<QueryArgument>
{
new QueryArgument<ListGraphType<IntGraphType>>
{
Name = "companyIds"
},
new QueryArgument<ListGraphType<StringGraphType>>
{
Name = "companyNames"
},
new QueryArgument<BooleanGraphType>
{
Name = "isActive"
},
new QueryArgument<IntGraphType>
{
Name = "page"
},
new QueryArgument<IntGraphType>
{
Name = "pageSize"
}
}),
resolve: delegate (ResolveFieldContext<object> context)
{
var columns = context.GetMainSelectedFields();
var companyIds = context.GetArgument<int?[]>("companyIds");
var companyNames = context.GetArgument<string[]>("companyNames");
var isActive = context.GetArgument<bool?>("isActive");
var page = context.GetArgument<int?>("page");
var pageSize = context.GetArgument<int?>("pageSize");
var request = new SearchCompanies()
{
Ids = companyIds,
Names = companyNames,
IsActive = isActive,
Page = page,
PageSize = pageSize,
Columns = columns
};
return companyService.GetCompanies(request);
});
#endregion
}
}
}
create-table.sql
-- auto-generated definition
create table Company
(
Id int identity
constraint Company_pk
primary key nonclustered,
Name nvarchar(200) not null,
IsActive bit default 1 not null
)
go
create unique index Company_Id_uindex
on Company (Id)
go