Repository Generic repository with Unit of Work for C# .net core with MongoDB

366 views Asked by At

I am new to .NET core and designing Asp.net core application layered architecture. We are using generic Repository with Unit of Work repository pattern. Completed the generic repository method, need to have some idea for Unit Of Work with collection.

Generic Repository

 public interface IMongoRepository<TDocument> where TDocument : IAudiableEntity
    {
       

        void InsertOne(TDocument document);


        Task<TDocument> InsertOneAsync(TDocument document);


        void InsertMany(ICollection<TDocument> documents);

        Task InsertManyAsync(ICollection<TDocument> documents);

        void ReplaceOne(TDocument document);

        Task ReplaceOneAsync(TDocument document);

        void DeleteOne(Expression<Func<TDocument, bool>> filterExpression);

        Task DeleteOneAsync(Expression<Func<TDocument, bool>> filterExpression);

        void DeleteById(string id);

        Task DeleteByIdAsync(string id);

        void DeleteMany(Expression<Func<TDocument, bool>> filterExpression);

        Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression);
    }

 public class MongoRepository<TDocument> : IMongoRepository<TDocument>
    where TDocument : IAudiableEntity
    {
        private readonly IMongoCollection<TDocument> _collection;

        public MongoRepository(IMongoDbSettings settings)
        {
            var database = new MongoClient(settings.ConnectionString).GetDatabase(settings.DatabaseName);
            _collection = database.GetCollection<TDocument>(GetCollectionName(typeof(TDocument)));
        }

        private protected string GetCollectionName(Type documentType)
        {
            return ((BsonCollectionAttribute)documentType.GetCustomAttributes(
                    typeof(BsonCollectionAttribute),
                    true)
                .FirstOrDefault())?.CollectionName;
        }

       


        public virtual void InsertOne(TDocument document)
        {
            _collection.InsertOne(document);
        }

        public virtual async Task<TDocument> InsertOneAsync(TDocument document)
        {
            await _collection.InsertOneAsync(document);
            return document;
        }

        public void InsertMany(ICollection<TDocument> documents)
        {
            _collection.InsertMany(documents);
        }


        public virtual async Task InsertManyAsync(ICollection<TDocument> documents)
        {
            await _collection.InsertManyAsync(documents);
        }

        public void ReplaceOne(TDocument document)
        {
            var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, document.Id);
            if (document is Slides slide)
            {
                slide.IsDirty = true;
            }
            _collection.FindOneAndReplace(filter, document);
        }
        public void ReplaceWODirtyOne(TDocument document)
        {
            var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, document.Id);
            _collection.FindOneAndReplace(filter, document);
        }
        public virtual async Task ReplaceOneAsync(TDocument document)
        {
            var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, document.Id);
            if (document is Slides slide)
            {
                slide.IsDirty = true;
            }
            await _collection.FindOneAndReplaceAsync(filter, document);
        }
        public virtual async Task ReplaceWODirtyOneAsync(TDocument document)
        {
            var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, document.Id);
            await _collection.FindOneAndReplaceAsync(filter, document);
        }
        public void DeleteOne(Expression<Func<TDocument, bool>> filterExpression)
        {
            _collection.FindOneAndDelete(filterExpression);
        }

        public Task DeleteOneAsync(Expression<Func<TDocument, bool>> filterExpression)
        {
            return Task.Run(() => _collection.FindOneAndDeleteAsync(filterExpression));
        }

        public void DeleteById(string id)
        {
            var objectId = new ObjectId(id);
            var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, objectId);
            _collection.FindOneAndDelete(filter);
        }

        public Task DeleteByIdAsync(string id)
        {
            return Task.Run(() =>
            {
                var objectId = new ObjectId(id);
                var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, objectId);
                _collection.FindOneAndDeleteAsync(filter);
            });
        }

        public void DeleteMany(Expression<Func<TDocument, bool>> filterExpression)
        {
            _collection.DeleteMany(filterExpression);
        }

        public Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression)
        {
            return Task.Run(() => _collection.DeleteManyAsync(filterExpression));
        }
    }

How can do the Unit of work here, let me know the possibilities.

2

There are 2 answers

1
Mcan_Cicek On

It will be better for you to understand if I answer your question by giving an example.

Let's assume you have a repository named 'ApplicationUser'

Your interface will look like this

public interface IApplicationUserRepository : IMongoRepository<ApplicationUser>
    {

    }

And Your Repository is here;

public class ApplicationUserRepository : IMongoRepository<ApplicationUser>, IApplicationUserRepository
{
    public ApplicationUserRepository(EFContext context) : base(context)
    {

    }
}

İf you want to use UnitOfWork pattern, you should add your interface inside to IUnitOfWork class

   public interface IUnitOfWork : IDisposable
    {
        IApplicationUserRepository ApplicationUserRepository { get; set; }
        EFContext Context { get; }
        List<string> Tables { get; }
        List<string> Columns(string tableName);
        int Commit();
    }

The example may not suit your structure, but the structure you need to create will look like this. Also, it doesn't matter which database you use for EF.

2
JHBonarius On

I would ditch the "generic repository" (often considered an anti-pattern, especially the way you are doing it) and use the MongoDB driver directly, as the MongoCollection already is a repository. You can then use the transaction functionality it has to offer.

The other answer suggested some Entity Framework solution. Yes, you can now use EF Core with MongoDB since they released a provider one week ago, but it's still in beta or early phases, so could have issues. Also, don't make it as complex as he suggests. Keep it simple.