MONGODB .NET (C#) ENTEGRASYONU

Öncelikle C# entegrasyonu için kendiniz mongodb driverlarını kullanarak geliştirme yapabileceğiniz gibi mongorepository gibi open source kütüphanelerde mevcuttur.Codeplextten mongorepository ait dllleri indirerek bizim kendi örneğimizde yapacağımız pek çok işlemi yapabilirsiniz.

https://mongorepository.codeplex.com/SourceControl/latest

Ancak biz hem esneklik hem öğrenme amaçlı olarak bu makalede kendi sınıflarımızı yazacağız ama pek çok noktada mongorepository yazılmışı var cümlesini karşılamaktadır :)

Öncelikle mongodb .net driverlarını projemize eklememiz gerekiyor

Bunun için birkaç yöntem kullanabiliriz

İlk olarak github tan gerekli dllleri indirebilirsiniz.(En son release dosyalarını indirmeye dikkat edin)

https://github.com/mongodb/mongo-csharp-driver/releases

Ya da mongo dbnin nuget desteğide mevcuttur

Install-Package mongocsharpdriver

Biz kendi örneğimiz için bir tane Windows application projesi açarak nuget yardımıyla güncel driverımızı indirelim

mongonet1

Nuget ile dlleri kendimizi aldıgımızda proje referanslarımızda mongodb dllerini görüyor olacağız.

mongonet2

Ancak yinede kendi mongo db kütüphanemizi yazarken githubta bulunan örnek repositorylerden faydalanıyor olacağız.

https://github.com/rsingh85/MongoDbRepository

Öncelikle bir tane base nesne classı oluşturalım,mongo dbye gidecek bütün nesnelerimiz entitybase türetiyor olacağız.Mongodb collectionda bulunan her bir documentin bir idsi vardır ve deserialize edilirken bu idde deserialize edilir.Bsonid attribute ile ilgili propertimizin bu dökümana ait identification alanı oldugunu belirttik

namespace MongoDbSample.MongoRepository
{
    public  class EntityBase
    {
        [BsonId]
        public string Id { get; set; }
    } 
}

Not: Esasta bson attribute yerine direkt Objectid kullanabilirdik ancak json deserialize ederken bu şekilde kullanım size sorun çıkartıyor.Bu yüzden biz serialize işlemlerinde sorun yaşamamak için string bir alan tanımladık.ve bunu BsonId attribute ile işaretledik.

public ObjectId Id { get; set; }
İkinci olarak repository pattern yapısına uygun olarak bir tane repository interface hazırlayım.Bu interfacede temel crud işlemleri ve search ve getbyid gibi detay getiren fonksiyonlarımız olsun.

namespace MongoDbSample.MongoRepository
{
    public interface IRepository<TEntity> where TEntity : EntityBase
    {
        WriteConcernResult Insert(TEntity entity);
        WriteConcernResult Update(TEntity entity);
        WriteConcernResult Delete(TEntity entity);
        IList<TEntity>
            SearchFor(Expression<Func<TEntity, bool>> predicate);
        IList<TEntity> GetAll();
        TEntity GetById(string id);
    } 
}

Bu interfacede değişik olarak dikkatinizi çeken nokta WriteConcernResult olmuştur.WriteConcernResult ile mongodbden işleminize ait dönüş bilgisini alabilirsiniz.
WriteConcernResultla mongodb de hata oluşmussa nedenini vs gibi bilgilerini elde edebilirsiniz

mongonet3

Şimdide bu interfaceden türeyecek kendi repositorymizi MongoDbRepository ismiyle oluşturalım

using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using MongoDB.Driver.Linq;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace MongoDbSample.MongoRepository
{
    public class MongoDbRepository<TEntity> :IRepository<TEntity> where TEntity : EntityBase
    {
        private MongoDatabase database;
        private MongoCollection<TEntity> collection;

        public MongoDbRepository()
        {
            GetDatabase();
            GetCollection();
        }

        public WriteConcernResult Insert(TEntity entity)
        {
            entity.Id = ObjectId.GenerateNewId().ToString();
            WriteConcernResult result = collection.Insert(entity);
            return result;
        }

        public WriteConcernResult Update(TEntity entity)
        {
            if (entity.Id == null)
                return Insert(entity);

           WriteConcernResult result=collection
                .Save(entity);
           return result;
                  
        }

        public WriteConcernResult Delete(TEntity entity)
        {
            WriteConcernResult result=collection
                .Remove(Query.EQ("_id", entity.Id));
            return result;
                    
        }

        public IList<TEntity>
            SearchFor(Expression<Func<TEntity, bool>> predicate)
        {
            return collection
                .AsQueryable<TEntity>()
                    .Where(predicate.Compile())
                        .ToList();
        }

        public IList<TEntity> GetAll()
        {
            return collection.FindAllAs<TEntity>().ToList();
        }

        public TEntity GetById(string id)
        {
            return collection.FindOneByIdAs<TEntity>(id);
        }

        #region Private Helper Methods
        private void GetDatabase()
        {
            var client = new MongoClient(GetConnectionString());

            var server = client.GetServer();

            database = server.GetDatabase(GetDatabaseName());
            }

        private string GetConnectionString()
        {
            return ConfigurationManager
                .AppSettings
                    .Get("MongoDbConnectionString")
                        .Replace("{DB_NAME}", GetDatabaseName());
        }

        private string GetDatabaseName()
        {
            return ConfigurationManager
                .AppSettings
                    .Get("MongoDbDatabaseName");
        }

        private void GetCollection()
        {
            collection = database
                .GetCollection<TEntity>(typeof(TEntity).Name);
        }
        #endregion
    } 
}

Repositorymizi yukarıdaki gibi yazdıktan sonra database bağlantı bilgilerimizi aşağıdaki gibi app.configimize ekleyelim

mongonet4

Şimdi bir kişi ve bu kişiye ait mailleri tutan model sınıflarımızı yazalım.Burda kilit nokta eklemek istediğimiz ana nesnenin EntityBaseden türeyerek bir bsonid yani mongodb id alanına sahip olmasıdır.

namespace MongoDbSample.Model
{
    public class Person:EntityBase
    {
        public string Name { get; set; }

        public string Surname{get;set;}

        public List<Mail> Mails { get; set; }
    }
}

namespace MongoDbSample.Model
{
    public class Mail
    {
        public string MailAddress { get; set; }

        public bool IsBusiness{get;set;}
    }
}

Sınıflarımızı oluşturdak sonra ui projemize bir tane button ekleyip mongo dbye ilk kaydımızı ekleyelim

private void btnInsert_Click(object sender, EventArgs e)
        {
            Person p = new Person();
            p.Name = "Bilgehan";
            p.Surname = "Yıldız";
            p.Mails = new List<Mail>()
            {
            new Mail(){ IsBusiness=true, MailAddress="abc@d.com"},
            new Mail(){ IsBusiness=false, MailAddress="e@f.com"},
            };

            MongoDbRepository<Person> Repository = new MongoDbRepository<Person>();
           WriteConcernResult result= Repository.Insert(p);
           if (!result.HasLastErrorMessage)
           {
               MessageBox.Show("Kayıt başarı ile eklendi");
           }
        }

mongodb de bakalım
mongonet5

Görüldüğü üzere oluşturdugumuz sınıfın adında ve sınıftaki propertylere göre veritabanımıza kayıt eklendi peki biz eğer veritabanındaki kolonlarla .net sınıfmızdaki alanların farklı olmasını istersek.

Temelde baktıgımızda mongodbye kayıt atmak ve kaydı okumak serialization işlemleri dolayısıyla burada nasıl ki datacontract serializerda propertylere alias vermek için [DataMember(Name=”xxxxx “)] gibi yöntemler kullanıyorsak ise BsonElement kavramı devreye
devreye giriyor örneğimizi test etmek amaclı Mail sınıfında bulunan IsBusiness alanını IsBusinessMail yapalım ama geçmiş kayıtlarla uyumlu olması açısından database bu alanın eski adıyla IsBusiness olarak atılmasını istiyorum BsonElementin attributeın name özelliği burada devreye giriyor

public class Mail
    {
        public string MailAddress { get; set; }
        [BsonElement("IsBusiness")] 
        public bool IsBusinessMail{get;set;}
    }

Şimdi Windows applicationdaki kayıt ekleme örneğimizdede alanın adını IsBusinesstan IsBusinessMaile çevirelim ve tekrar çalıştıralım.

private void btnInsert_Click(object sender, EventArgs e)
        {
            Person p = new Person();
            p.Name = "Ümit";
            p.Surname = "Gündüz";
            p.Mails = new List<Mail>()
            {
            new Mail(){ IsBusinessMail=true, MailAddress="mybusinessmail@d.com"},
            new Mail(){ IsBusinessMail=false, MailAddress="mypersonelmail@f.com"},
            };

            MongoDbRepository<Person> Repository = new MongoDbRepository<Person>();
           WriteConcernResult result= Repository.Insert(p);
           if (!result.HasLastErrorMessage)
           {
               MessageBox.Show("Kayıt başarı ile eklendi");
           }
        }

 

 

mongonet6

Görüldüğü üzere propertyimize bir nevi alias vermiş olduk.Bu şekilde serializable işlemlerinde size yardımcı olabilecek farklı attributelarda mevcuttur

mongonet7

Örnek olarak .net classınıza koydugunuz ancak database kaydının atılması istediğiniz alan varsa BsonIgnore attribute kullanabilirsiniz tıpkı xml serialization işlemlerinde kullandınız XmlIgnore attribute gibi

mongonet8

Veritabanında hangi alana collectiona kayıt atacağımız ise repositorydeki getcollection metodundan kalıyor bizim mantıgımızda classın adı collectionın adına eşit oluyor ancak isterseniz custom attributelar yapıp bu metodu değiştirerek istediğiniz collection adına göre kayıt işlemi yapmanızda mümkün.

Mesela mongorepository kütüphanesinde CollectionName diye bir attribute yaratıp bu attribute varsa collection adını buradan al yoksa sınıf adından al gibi bir yöntem geliştirmişler ,kendi mongo kütüphanenizi yazacaksanız bile mongorepository open source açıp kodlarına bakmak faydalı olabilir.

[AttributeUsage(AttributeTargets.Class, Inherited = true)]
    public class CollectionName : Attribute
    {

…………


  private static string GetCollectioNameFromInterface<T>()
        {
            string collectionname;

            // Check to see if the object (inherited from Entity) has a CollectionName attribute
            var att = Attribute.GetCustomAttribute(typeof(T), typeof(CollectionName));
            if (att != null)
            {
                // It does! Return the value specified by the CollectionName attribute
                collectionname = ((CollectionName)att).Name;
            }
            else
            {
                collectionname = typeof(T).Name;
            }

            return collectionname;
        }




Diğer metodlarımızada kısaca göz atalım
Örnek search metodumuz linq sorgusu yazar gibi kullanabiliyoruz

private void btnSearch_Click(object sender, EventArgs e)
        {

            MongoDbRepository<Person> Repository = new MongoDbRepository<Person>();

           var Searchlist =  Repository.SearchFor(p=>p.Name.Contains("Bilgehan"));
        }

mongonet9

Örnek olarak mongodbde bir kayıt güncelleme kaydın son halini çekip yeni bir mail ekleme örneği yapalım

private void btnUpdate_Click(object sender, EventArgs e)
        {
            MongoDbRepository<Person> Repository = new MongoDbRepository<Person>();
          Person p=  Repository.GetById("553365800529210aa0a24054");
          p.Mails.Add(new Mail() { IsBusinessMail = false, MailAddress = "extraaddedmail@f.com" });
          WriteConcernResult result = Repository.Update(p);
          if (!result.HasLastErrorMessage)
          {
              MessageBox.Show("Kayıt başarı ile güncellendi");
          }
        }

 

mongonet10

Bu şekilde de bu yazımızda mongodb c# entegrasyonun temel halini görmüş olduk.

Bu yazı .NET, MONGODB kategorisine gönderilmiş. Kalıcı bağlantıyı yer imlerinize ekleyin.

MONGODB .NET (C#) ENTEGRASYONU için 10 cevap

  1. Cebeli Tarık der ki:

    At gibi olmuş

  2. Bülent ERDEM der ki:

    Ellerinize sağlık hocam Çok güzel ayrıntılı çalışma olmuş.Asenkron metodlar malumunuz yeni driverlara konuldu. Onunla ilgili bir çalışmanız var mı? Saygılarımla….

    • bilgehanyildiz der ki:

      Merhabalar yeni driverla beraber bu makaledeki bazı metodlar (minor değişikler) değişti.

      Asenkronlarla ilgili bir çalışma yapmadım maalesef.İlginiz için teşekkür ederim.

  3. Kazım der ki:

    Merhaba, gayet güzel bir makale ellerinize sağlık fakat yeni sürümle beraberinde bir sürü problem getirmekte. Bununla ilgili bir çalışmanız var mı? Bilgi verebilir misiniz?
    Teşekkürler.

    • bilgehanyildiz der ki:

      Merhabalar,evet haklısınız mongodb .net library 2.0 lı versiyonları ile epey bir farklılık getirmiş durumda repository bazındada artık MongoCollection yerine IMongoCollection üzerinden işlemlerimizi yapmamız gerekiyor,ancak asenkron metodlar ve iç içe sorgular konusunda yenilikler getiriyor.Konu ile alakalı olarak kendi projelerimde yeni sürüme geçip kullandım ,ancak henüz makale olarak yazma fırsatım olmadı.Özetle IMongoCollection üstünden repositorynizi kurgulamanız 1.0larda olan özellikle insert,update,deletelerde kullanılan WriteConcernResult classından vazgeçmemiz gerekiyor.Araştırdıgım kadarıyla 3.0 kütüphanesi çıkana kadar yine 1.0 daki bu özellikler kullanılabilecek.iyi Çalışmalar

  4. Sinan der ki:

    Selamlar,

    Çok güzel ve faydalı bir çalışma olmuş, elinize sağlık.

  5. BAU der ki:

    Merhaba
    Değerli bilgilerinizi bizimle paylaştığınız için teşekkür ederim. Yalnız size bir sorum olacak. Sipariş>Sipariş Stokları>Stok şeklinde 3 tane class ım tablom var. Entityde sipariş stokları classına tanımlamış olduğum stok virtual property sinden stok ile ilgili bilgileri alabiliyorum. Mongoda yapı nasıldır, nasıl tasarlamak gerekiyor?

    Bir sipariş stoğu eklendiğinde stok değerlerini her kaleme atmak mantıksız geliyor(Mongo yapısı bu şekilde ama).
    Bu konu hakkındaki değerli bilgilerinizi rica ederim.

    • bilgehanyildiz der ki:

      Mongodb yapısı gereği döküman base tabanlıdır.Ve cok fazla update yapılması önerilmez.Yapın şöyle olsa Kişi->Siparişleri->Sipariş yorumları gibi hepsi aynı kişi altında tek dökümanda tutulabilirsin.Mongodb de de bahsettiğin sipariş ve stokları ayrı tablolarda tutuabilirsin.Ama baktıgın zaman stok direkt kişi nesnesinin altında değil ,bu durumdada ilişkisel veritabanındadaki gibi belli keylerle tabloları ilişkilendirmen lazım.Update maliyetini düşünerek tasarım yapmanız gerekli burada.Mesela uygulamana ait logları tutmak istersen mongodb iyi bir alternatif.Çünkü logu sen bir bütün olarka tek bir objede tutacaksın kolay sorgulama yapabileceksin ama logun updateti olmayacak.

  6. Oğuz der ki:

    Merhabalar. Makaleniz güzel olmuş. Ancak bunu “Dependency Injection” mantığı ile nasıl yöneteceğiz? Bu kısım eksik olmuş gibi.

    • bilgehanyildiz der ki:

      Merhaba mongodb driver yapısı değişti yeni yapı ile beraber WriteConcernResultlar da kalktı dolayısıyla dependency injection uyumlu genel classları daha kolay yapabilirsiniz.Makaleden bağımsız olarak dependency injection uyumlu yapılar yapmak başka bir makale konusu :)

bilgehanyildiz için bir cevap yazın Cevabı iptal et

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Şu HTML etiketlerini ve özelliklerini kullanabilirsiniz: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>