Categories: ASP.NET CoreGraphQL

GraphQL’e Giriş ve ASP.NET Core 2.0 ile Basit Bir Query API Tasarlamak

Merhaba arkadaşlar.

Bir süredir yeni bir makale yazmaya fırsat bulamadım. Hatta bu makalenin bir kısmını ise Ağustos ayında yazmıştım, fakat bir türlü tamamlayamamıştım. 🙂 Bulduğum ilk fırsatta ise tamamlamayı başardım.

Herneyse, sanırım GraphQL (ayrıca Asp.NET Core 2.0), veri erişimi ve sorgulama üzerine son dönemlerin en popüler konuları arasındadır. GraphQL özellikle günümüz çağında hızla gelişen bu business ihtiyaçlarına karşı, veri erişimini ve gelişmiş sorgulamasını client tarafına bırakarak, hızlı ve efficient bir şekilde development yapabilme imkanını biz developer’lara sunmaktadır.

Öncelikle GraphQL konusu, benim içinde yeni sayılabilecek bir konu. GraphQL ile henüz production üzerinde bir tecrübe edinemedim. Fakat yakın bir dönem içerisinde özellikle mobile API’larımız için bir gateway gibi GraphQL ile veri erişimi ve sorgulama işlemlerini gerçekleştirebilmeyi planlıyoruz. (Şuan üzerinde çalışıyoruz)

Bu makale içerisinde ise ASP.NET Core 2.0 ile GraphQL‘i kullanarak, sorgulama işlemlerini efficient bir şekilde nasıl gerçekleştirebiliriz konusuna, araştırmalarım sırasında edinebildiğim bilgiler doğrultusunda değinmeye çalışacağım.

Peki, Neden GraphQL?

Neden GraphQL sorusuna geçmeden önce, bir konuya değinmek istiyorum. Bir çok kişi REST‘in öleceği ve GraphQL‘in ise yeni gelecek olduğundan bahsediyor. Ben şahsen bu fikre katılmıyorum. REST ve GraphQL‘in birbirleri ile karıştırılmaması gerektiği düşüncesindeyim.

İlk olarak hem REST hem de GraphQL‘in bir arada kullanımı mümkündür. Ayrıca GraphQL doğru zaman ve doğru ihtiyaçlar karşısında kullanıldığında, veri erişimi (özellikle mobile tarafı için) ve querying konusunda client’lar için oldukça esneklik sağlamaktadır.

Peki, neden GraphQL kısmına şöyle bir giriş yapayım. Günümüz teknolojisinde mobile uygulamalarının kullanım oranının, oldukça fazla olduğu ortada. Çalışmış olduğum firmalarda, online kullanıcıların bir çoğunu neredeyse mobile kullanıcılar oluşturmakta. Development tarafına baktığımızda ise uygulamalarımızın building block’larını oluşturan API‘ları, hem web hemde mobile tarafları için kullanmaktayız.

İşte tam da bu noktada, özellikle mobile tarafı için bazı challenge’lar ile karşılaşmaktayız. Çünkü mobile tarafı için alınan data boyutu, pil ve şebeke kullanımı gibi durumlar büyük önem taşımaktadır. Genelde bu tarz challenge’ları, API‘ların önüne Gateway API‘lar yazarak aggregation işlemlerini gateway içerisinde gerçekleştirip, developer’ın istediği data’ları filtreleyerek alabilmesi ile atlatırız. Kulağa hoş gelse de, ne yazık ki iş burada bitmiyor. 🙂 Bununla birlikte versiyonlama ve backward compatibility gibi vb. challenge’larla da karşılaşmak durumunda kalabiliyoruz.

İşte bu gibi durumlar karşısında GraphQL‘in bizlere sunduğu aşağıdaki gibi bazı avantajlar bulunmaktadır.

  • Client’a ihtiyaç duyduğu data’yı, tek bir request ile kolaylıkla alabilme imkanı sunmaktadır
  • Aggregation yapılan API gateway’ler gibi tailored endpoint’lere olan ihtiyacı azaltmaktadır
  • En önemlisi de hızlı ve kolay development yapabilme imkanını sunmaktadır

Hızlı ve kolay development konusundan biraz bahsetmek gerekirse eğer, bir çok kurum içerisinde development ekipleri küçük domain’lere göre bölünüp, her ekip kendi sorumluluğunun bulunduğu uygulamaları geliştirmektedir. Benim çalıştığım bir kaç firmada bu şekildeydi.

Düşünelim, ürün detay sayfasında “1” numaralı ürünün detay bilgilerine ihtiyacımız var. Bir RESTful API içerisinde normal şartlarda bu bilgileri “api/products/1” şeklinde bir endpoint’e GET isteğinde bulunarak, kolaylıkla erişebiliriz. Mobile ekibin ise bu ürün bilgilerinden sadece “name“, “description” ve “price” field’larına ihtiyacı olup, bunlara ek olarak birde ürünün kategori bilgilerine de ihtiyacı olduğunu varsayalım.

Bu tarz durumlar karşısında, yapabileceğimiz iki farklı seçenek bulunmaktadır.

  1. İki farklı request gerçekleştirip, “roundtrips” yapmak (Söz konusu mobile side ve veri kullanımı olunca çok fazla tercih etmeyiz sanırım?)
  2. Ve ya bu işlemi tailored bir endpoint oluşturarak gerçekleştirmek

Bunlara ek olarak birde her ekibin kendi sprint’lerini koştuğunu varsayarsak, mobile ekip bu noktada “Product” API‘ından sorumlu ekibin ilgili development’ı yapmasını da bekleyecektir. Hızla yeni feature’lar eklemek istediğimiz bu teknoloji pazarında ise, beklemek çok da hoş olmayacaktır, değil mi?

GraphQL ile bir API tasarlandığında ise, mobile ekip diğer ekiplere bağımlı olmadan istediği data’yı tek bir request ile efficient bir şekilde sorgulayarak alabilmesi, hızlı bir şekilde ihtiyaçlarına göre development işlemlerini gerçekleştirebilmesi kolay bir hale gelmektedir.

Örnek bir GraphQL query’sine bakmak istersek eğer:

{
  product(id: 1){
    name
    description
    price
    category{
      id
      name
    }
  }
}

Yukarıdaki gibi single bir query ile istenilen data, client-driven generated bir şekilde kolaylıkla alınabilmektedir.

NOT: Bu işlemleri elbette bir RESTful API ile de yapabilmek mümkündür. (Querying, filtering, sorting, vb…) Fakat her yeni bir ihtiyaç karşısında istenilen feature’ın, RESTful API‘a implemente edilmesi veya client tarafında handle edilmesi gerekmektedir. (Roundtrips!) GraphQL bu noktada ön plana çıkarken, unutmamalıyız ki GraphQL‘in istenilenleri gerçekleştirebilmesi için ise, doğru bir mapping yapısına da sahip olması gerekmektedir.

ASP.NET Core 2.0 ile Implementasyon

Implementasyon aşamasında ise, örnek olarak basit bir “ürün” ve “kategori” içeren bir API tasarlayacağız.

Bunun için öncelikle aşağıdaki komut satırı ile yeni bir ASP.NET Core “webapi” projesi oluşturalım.

mkdir aspnetcoregarphql
cd aspnetcoregarphql
dotnet new webapi

Projenin oluşturulmasından sonra “Models” isminde bir klasör oluşturarak, “Category” ve “Product” model’lerini burada tanımlayalım.

namespace aspnetcoregraphql.Models
{
    public class Category
    {
        public int Id { get; set; }
        public string Name { get; set; }

        List<Product> Products { get; set; }
    }
}
namespace aspnetcoregraphql.Models
{
    public class Product
    {
        public int Id { get; set; }
        public int CategoryId { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public double Price { get; set; }

        Category Category { get; set;}
    }
}

Modelleri tanımlamanın ardından “Data” isimli bir klasör daha oluşturalım. İçerisinde ise “ICategoryRepository” ve “IProductRepository” interface’lerini aşağıdaki gibi oluşturalım.

namespace aspnetcoregraphql.Data
{
    public interface ICategoryRepository
    {
        Task<List<Category>> CategoriesAsync();
        Task<Category> GetCategoryAsync(int id);
    }
}
namespace aspnetcoregraphql.Data
{
    public interface IProductRepository
    {
        Task<List<Product>> GetProductsAsync();
        Task<List<Product>> GetProductsWithByCategoryIdAsync(int categoryId);
        Task<Product> GetProductAsync(int id);
    }
}

Bu interface’lerin implementasyonlarını ise, test data’ları ile birlikte aşağıdaki gibi gerçekleştirelim.

namespace aspnetcoregraphql.Data
{
    public class CategoryRepository : ICategoryRepository
    {
        private List<Category> _categories;

        public CategoryRepository()
        {
            _categories = new List<Category>{
                new Category()
                {
                    Id = 1,
                    Name = "Computers"
                },
                new Category()
                {
                    Id = 2,
                    Name = "Mobile Phones"
                }
            };
        }

        public Task<List<Category>> CategoriesAsync()
        {
            return Task.FromResult(_categories);
        }

        public Task<Category> GetCategoryAsync(int id)
        {
            return Task.FromResult(_categories.FirstOrDefault(x => x.Id == id));
        }
    }
}
namespace aspnetcoregraphql.Data
{
    public class ProductRepository : IProductRepository
    {
        private List<Product> _products;

        public ProductRepository()
        {
            _products = new List<Product>{
                new Product()
                {
                    Id = 1,
                    CategoryId = 1,
                    Name = "Apple Macbook Pro 2016",
                    Description = "Touchbar, 3.2GHZ",
                    Price = 5000
                },
                new Product()
                {
                    Id = 2,
                    CategoryId = 1,
                    Name = "Apple Macbook Air",
                    Description = "2.3GHZ 8GB RAM",
                    Price = 2000
                },
                new Product()
                {
                    Id = 3,
                    CategoryId = 1,
                    Name = "Dell XPS 13",
                    Description = "3.3GHZ 12GB RAM",
                    Price = 4000
                }
            };
        }

        public Task<Product> GetProductAsync(int id)
        {
            return Task.FromResult(_products.FirstOrDefault(x => x.Id == id));
        }

        public Task<List<Product>> GetProductsAsync()
        {
            return Task.FromResult(_products);
        }
       
        public Task<List<Product>> GetProductsWithByCategoryIdAsync(int categoryId)
        {
            return Task.FromResult(_products.Where(x => x.CategoryId == categoryId).ToList());
        }
    }
}

Model ve repository’ler hazır olduğuna göre, artık GraphQL‘in .NET client’ını implemente etmeye başlayabiliriz.

Bunun için öncelikle aşağıdaki komutu kullanarak, projemize GraphQL paketini NuGet üzerinden dahil edelim.

dotnet add package GraphQL

Paketi projemize ekledikten sonra, ilk olarak GraphQL Object Type‘larını tanımlamaya başlayalım.  Object type‘ları ve field’lar GraphQL schema’sının en temel bileşenlerini oluşturmaktadır. Yani, oluşturacağımız servis üzerinden hangi objeleri alabileceğimizi ve hangi field’lara sahip olduğunu belirtmektedir.

Bu ön bilginin ardından “Models” klasörü içerisinde, “CategoryType” ve “ProductType” class’larını da aşağıdaki gibi oluşturalım.

namespace aspnetcoregraphql.Models
{
    public class CategoryType : ObjectGraphType<Category>
    {
        public CategoryType(IProductRepository productRepository)
        {
            Field(x => x.Id).Description("Category id.");
            Field(x => x.Name, nullable: true).Description("Category name.");

            Field<ListGraphType<ProductType>>(
                "products", 
                resolve: context => productRepository.GetProductsWithByCategoryIdAsync(context.Source.Id).Result.ToList()
            );
        }
    }
}
namespace aspnetcoregraphql.Models
{
    public class ProductType : ObjectGraphType<Product>
    {
        public ProductType(ICategoryRepository categoryRepository)
        {
            Field(x => x.Id).Description("Product id.");
            Field(x => x.Name).Description("Product name.");
            Field(x => x.Description, nullable: true).Description("Product description.");
            Field(x => x.Price).Description("Product price.");

            Field<CategoryType>(
                "category", 
                resolve: context => categoryRepository.GetCategoryAsync(context.Source.CategoryId).Result
             );
        }
    }
}

Dikkat edersek her iki class da, “ObjectGraphType” class’ından türetilmektedir. Sonrasında ise constructor içerisinden GraphQL paketinin fluent method’larını kullanarak, hangi field’lara sahip olması gerektiğini expression’lar ile tanımladık. Ardından relation field’ları için ise resolve function’larını yazdık.

Şimdi query işlemleri için kullanacak olduğumuz root type’ı oluşturacağız. Bunun için “EasyStoreQuery” isminde yeni bir class tanımlayalım ve aşağıdaki gibi kodlayalım.

namespace aspnetcoregraphql.Models
{
    public class EasyStoreQuery : ObjectGraphType
    {
        public EasyStoreQuery(ICategoryRepository categoryRepository, IProductRepository productRepository)
        {
            Field<CategoryType>(
                "category",
                arguments: new QueryArguments(
                    new QueryArgument<NonNullGraphType<IntGraphType>> {Name = "id", Description = "Category id"}
                ),
                resolve: context => categoryRepository.GetCategoryAsync(context.GetArgument<int>("id")).Result
            );

            Field<ProductType>(
                "product",
                arguments: new QueryArguments(
                    new QueryArgument<NonNullGraphType<IntGraphType>> {Name = "id", Description = "Product id"}
                ),
                resolve: context => productRepository.GetProductAsync(context.GetArgument<int>("id")).Result
            );
        }
    }
}

EasyStoreQuery” root type’ı içerisinde, “CategoryType” ve “ProductType” da olduğu gibi constructor içerisinde schema’yı configure ettik. Configure işlemi sırasında “arguments” parametresi ile ise, ilgili field’ın hangi argument’ler doğrultusunda alınabileceğini belirttik.

NOT: “CategoryType“, “ProductType” ve “EasyStoreQuery” içerisindeki resolve function’larının source’u olarak “category” ve “product” repository’lerini configure etmek yerine, “category” ve ya “product” için olan ilgili REST endpoint’lerini de configure edebilirdik.

Artık dışarıya expose edecek olduğumuz data’mızın, structure’ını tanımlayacak olan schema’yı oluşturabiliriz. Bunun için “EasyStoreSchema” adında bir class daha oluşturalım ve “Schema” class’ından türeterek, aşağıdaki gibi kodlayalım.

namespace aspnetcoregraphql.Models
{
    public class EasyStoreSchema : Schema
    {
        public EasyStoreSchema(Func<Type, GraphType> resolveType)
            :base(resolveType)
        {
            Query = (EasyStoreQuery)resolveType(typeof(EasyStoreQuery));
        }
    }
}

Bir schema’nın sahip olabileceği iki adet temel type bulunmaktadır. Bunlardan ilki “Query”, diğeri ise “Mutation” type’ıdır. Biz ise burada sadece “Query” type’ını kullandık. “EasyStoreSchema” class’ını DI container üzerinden inject ederken, resolve type olarak ise “EasyStoreQuery” type’ını parametre olarak geçeceğiz.

Artık geriye yapmamız gereken iki şey kaldı. Bunlardan ilki GraphQL endpoint’ini hazırlamak, bir diğeri de service injection işlemlerini gerçekleştirmek. Endpoint’i hazırlamak için öncelikle “Models” klasörü içerisinde “GraphQLQuery” isminde bir request class’ı oluşturalım ve aşağıdaki gibi tanımlayalım.

namespace aspnetcoregraphql.Models
{
    public class GraphQLQuery
    {
        public string OperationName { get; set; }
        public string NamedQuery { get; set; }
        public string Query { get; set; }
        public string Variables { get; set; }
    }
}

Request objesini tanımlamanın ardından, “Controllers” klasörü içerisinde ise “GraphQLController” isminde bir controller class’ı ekleyerek kodlamaya başlayalım.

namespace aspnetcoregraphql.Controllers
{
    [Route("graphql")]
    public class GraphQLController : Controller
    {
        private readonly IDocumentExecuter _documentExecuter;
        private readonly ISchema _schema;

        public GraphQLController(IDocumentExecuter documentExecuter,ISchema schema)
        {
            _documentExecuter = documentExecuter;
            _schema = schema;
        }

        [HttpPost]
        public async Task<IActionResult> Post([FromBody]GraphQLQuery query)
        {
            if (query == null) { throw new ArgumentNullException(nameof(query)); }

            var executionOptions = new ExecutionOptions { Schema = _schema, Query = query.Query };

            try
            {
                var result = await _documentExecuter.ExecuteAsync(executionOptions).ConfigureAwait(false);

                if (result.Errors?.Count > 0)
                {
                    return BadRequest(result);
                }

                return Ok(result);
            }
            catch(Exception ex)
            {
                return BadRequest(ex);
            }
        }
    }
}

İlk olarak “Route” attribute’ü ile endpoint’i belirledik ve ardından “IDocumentExecuter” ve “ISchema” interface’lerinin inject işlemlerini gerçekleştirdik. Bu noktada “IDocumentExecuter“, “ExecutionOptions” parametresini execute ederek, client’ın istemiş olduğu data’yı oluşturacaktır. Bu noktada, client-driven application development yapabilmek, çok da hoş değil mi?

Artık geriye sadece dependency injection işlemi kaldı. “Startup” class’ı içerisinde injection işlemlerini aşağıdaki gibi gerçekleştirelim.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddScoped<EasyStoreQuery>();   
    services.AddTransient<ICategoryRepository, CategoryRepository>();
    services.AddTransient<IProductRepository, ProductRepository>();   
    services.AddScoped<IDocumentExecuter, DocumentExecuter>();
    services.AddTransient<CategoryType>();
    services.AddTransient<ProductType>();
    var sp = services.BuildServiceProvider();
    services.AddScoped<ISchema>(_ => new EasyStoreSchema(type => (GraphType) sp.GetService(type)) {Query = sp.GetService<EasyStoreQuery>()});
}

İşte hepsi bu kadar.

Şimdi terminal üzerinden “dotnet run” komutunu çalıştırarak, bir kaç test yapabiliriz.

NOT: Ben test işlemleri için “Postman” kullanacağım.

Aşağıdaki sorguyu GraphQL endpoint’ine, POST olarak gönderelim ve sonucunu görelim.

{ 
 "query":
  "query{
     category(id:1){
       id 
       name
     }
   }"
}

Response’a baktığımızda ise aşağıda gördüğümüz gibi, “1” numaralı kategoriye ait “id” ve “name” field’ları gelmiştir.

Peki ya bu kategoriye ait ürünleri, “id“, “name“, ve “price” field’ları ile gelmesini de istiyorsak, bu durumda ne olacak? Gayet basit. Tek yapmamız gereken, sorgumuzu aşağıdaki gibi güncellemek olacaktır.

{ 
 "query":
  "query{
     category(id:1){
       id 
       name
       products{
        id
        name
        price
       }
     }
   }"
}

Sonucu ise aşağıdaki gibi ortada.

Sonuç

GraphQL ile PoC yaparken gerçekten çok keyif aldım. Bu konudaki benim fikrim ise, eğer database schema’mız ve ya API design’larımız resource olarak kullanılmaya uygun ise, GraphQL’i implemente etmek özellikle client-driven application development için doğru bir tercih olacaktır. Ayrıca GraphQL client’a sadece istediği ölçüde data’yı verebilmesiyle beraber, server ile client arasındaki veri boyutunun küçülmesini de sağlamaktadır.

Örnek projeye ise buradan erişebilirsiniz: https://github.com/GokGokalp/ASP.NET-Core-2.0-GraphQL-Sample

Referanslar

https://github.com/graphql-dotnet/graphql-dotnet

http://graphql.org/learn/

Gökhan Gökalp

View Comments

  • Paylaşımız için teşekkür ederim ama kafamda oturmayan şeyler var :)
    GraphQL kullanarak web service üzerinde herhangi bir değişiklik yapmadan hızlıca bir query oluşturup istediğimiz columnları tanımlayarak data getirme işlemleri yapabiliyoruz, bu işlem bize ortam bazında avantajlar sağlıyor(response data size or extra columns).

    Peki burada bir spesifik bir filter vermek istediğimizde web service burada nasıl davranıyor. Örneğin şurada bir örnek var; https://www.howtographql.com/graphql-js/10-filtering/
    Queryde belirttiğimiz filter tanımları bir repository aracılığıyla db seviyesindemi filter yapıyor yoksa repositoryde örneğin GetProducts() methodundan dönen liste içerisindemi bir filter uyguluyor?

    Bu konuda biraz detay verebilir misiniz? Artı ve eksileri varmıdır?

    • Merhaba, teşekkür ederim öncelikle yorumunuz için. Bu işlemler aslında sizin ilgili source'u ve query argument'ları nasıl tanımladığınıza göre biraz değişiklik gösteriyor. Hatta relation'ların resolve kısımlarına yönelik bunun üzerine tartışılmış bir çok N+1 query sorusu, query optimizasyon vb. gibi çözümleri de mevcut. (Benim gördüğüm) Benim yapmış olduğum basit örnekte "id" argument'ı ile ilerlediğim için bir filtering söz konusu değil db tarafında, in-memory gerçekleşmektedir. Buradaki query argument'larını çoğaltabilir ve ya kendi query argument'larımız için graph type'lar oluşturabilir ve spesifik filter query'leri geçebilerek, predicate'ler ile de bir sorgu oluşturabiliriz db'ye hit etmeden önce veya ilgili servis'in bir filter endpoint'i varsa. Bu kullanmış olduğum .NET client'ı hala geliştirilmekte olan bir paket, ayrıca benim için de baya yeni bir konu süredir üzerinde çalışıyorum bu tarz concern'lere karşı, deneyimlerimi buradan paylaşmaya devam edeceğim.

      • Terimler arasında kaybolup gittim ama konuyla ilgili yeni makalelerinizi merakla bekliyorum :)
        Ama özetle istemciden sunucudaki ilgili endpointe gelen request içerisindeki filtreyi bir convert işlemiyle ilgili orm tool yada data çekmek için kullandığımız data toolu ne ise, onun filter yapısına uydurup data çekmek işi çözecektir diye anlıyorum. Tabi bu convert işlemindeki pradicateleri uyumlu bir şekilde otomatik çeviren bir modül yoksa durum biraz vahim olabilir.

        • Merhaba tekrar, evet. :) Örnek vermek gerekirse eğer: "EasyStoreQuery" içerisindeki argument'lere bakarsak, "new QueryArgument {Name = "id", Description = "Category id"}" şeklinde bir id parametresi ekliyoruz aslında. GraphQL'in .NET client'ı ile buraları şekillendirmek bize kalıyor biraz. :) Bende üzerinde çalışmaktayım halen, ilerleyen bölümlerde paylaşmaya devam edeceğim edinebildiğim farklı tecrübeleri. :)

  • Paylaşım için teşekkürler güzel açıklama. Ben yinede grapql in neden kullanılması gerektiğini tam anlayamadım. Linq le çekip filtring yapmamızdan bir farkı yok gibi görünnüyor. Belki burda linq expressioni direk mobilden geliyomuş gibi düşünebiliriz. Buda mobilci için backend geliştirme beklemesini azaltır gibi.

    • Hi Gin, What you mean by "a list Products"? We already return product list with the following query:

      {
      "query":
      "query{
      category(id:1){
      id
      name
      products{
      id
      name
      price
      }
      }
      }"
      }

      If you want to return a product list without category, you should implement it in "EasyStoreQuery" query class without "id" parameter. Then you can be querying.

  • I just listened to .netrocks podcast on graphql where it was mentioned that graphql never returns any other status than 200. Can you confirm and if required correct the code for error handling?

  • Merhaba, Öncelikle makale için teşekkür ederiz. Bu projeyi asp.net web api frameworkü ile çalıştıramadım.

    GraphQLController içerisinde Constructor içerisinde schema injection'ının nasıl yapmalıyız. Teşekkürler.
    var schema = new Schema { Query = new EasyStoreSchema(new Func(CategoryType))};

    • Merhaba ne tarz bir hata alıyorsunuz? Injection derken? Tanımlamasını Startup'da şu şekilde gerçekleştirdik makale için: services.AddScoped(_ => new EasyStoreSchema(type => (GraphType) sp.GetService(type)) {Query = sp.GetService()});

  • Merhabalar, Hangi sorguyu çalıştırmak istersem istiyim aşağıdaki gibi hata alıyorum.

    Sorgu :
    {
    "query":
    "query{
    category(id:1){
    Id
    name
    }
    }"
    }

    Aldığım hata :
    {
    "errors": [
    {
    "message": "Syntax Error GraphQL (2:2) Expected Name, found String \"query\"\n1: { \n2: \"query\":\n ^\n3: \"query{\n"
    }
    ]
    }

    Bir sorun mu var acaba

    • Merhaba kusura bakmayın geç cevap için, spam yorumlar arasında kaybolmuş. Eğer herşeyi aynı yaptı iseniz, kullandığınız NuGet paket versiyonu, benim kullandığım ile aynı mı? Bir değişiklikler olmuş olabilir farklı versiyon ise.

    • Merhaba evet, kullandığım editör'den kaynaklı. Bir çok kod'da > gibi semböller de otomatik siliniyor kullandığım multi-language editör'ünden kaynaklı. Çözemedim bir türlü.

Recent Posts

Containerized Uygulamaların Supply Chain’ini Güvence Altına Alarak Güvenlik Risklerini Azaltma (Güvenlik Taraması, SBOM’lar, Artifact’lerin İmzalanması ve Doğrulanması) – Bölüm 1

{:tr}Bildiğimiz gibi modern yazılım geliştirme ortamında containerization'ın benimsenmesi, uygulamaların oluşturulma ve dağıtılma şekillerini oldukça değiştirdi.…

8 ay ago

Identity & Access Management İşlemlerini Azure AD B2C ile .NET Ortamında Gerçekleştirmek

{:tr}Bildiğimiz gibi bir ürün geliştirirken olabildiğince farklı cloud çözümlerinden faydalanmak, harcanacak zaman ve karmaşıklığın yanı…

12 ay ago

Azure Service Bus Kullanarak Microservice’lerde Event’ler Nasıl Sıralanır (FIFO Consumers)

{:tr}Bazen bazı senaryolar vardır karmaşıklığını veya eksi yanlarını bildiğimiz halde implemente etmekten kaçamadığımız veya implemente…

2 yıl ago

.NET Microservice’lerinde Outbox Pattern’ı ile Eventual Consistency için Atomicity Sağlama

{:tr}Bildiğimiz gibi microservice architecture'ına adapte olmanın bir çok artı noktası olduğu gibi, maalesef getirdiği bazı…

2 yıl ago

Dapr ve .NET Kullanarak Minimum Efor ile Microservice’ler Geliştirmek – 02 (Azure Container Apps)

{:tr}Bir önceki makale serisinde Dapr projesinden ve faydalarından bahsedip, local ortamda self-hosted mode olarak .NET…

2 yıl ago