2011-08-23 1 views
27

Я знаю, что MongoDB не должен поддерживать единицу работы и т. Д. Но я думаю, что было бы неплохо реализовать репозиторий, в котором будут храниться только намерения (похожие на критерии), а затем зафиксировать их в БД. В противном случае в каждом методе вашего репозитория вам необходимо создать соединение с БД, а затем закрыть его. Если мы поместим соединение с БД в некоторый класс BaseRepository, то мы привяжем наш репозиторий к конкретному БД, и действительно сложно протестировать репозитории, чтобы проверить IoC, которые разрешают репозитории.Единица работы в mongodb и C#

Создает ли сеанс в MongoDB плохую идею? Есть ли способ отделить логику соединения от хранилища?

Вот код Коури Роб. Это хорошая идея всегда подключаться к вашей БД по каждому запросу? Какова наилучшая практика?

Есть еще одна вещь. Представьте, что я хочу предоставить индекс для коллекции. Раньше я делал это в конструкторе, но с подходом Роба это кажется логичным, чтобы сделать это там.

using Norm; 
    using Norm.Responses; 
    using Norm.Collections; 
    using Norm.Linq; 

    public class MongoSession { 

     private string _connectionString; 

     public MongoSession() { 
      //set this connection as you need. This is left here as an example, but you could, if you wanted, 
      _connectionString = "mongodb://127.0.0.1/MyDatabase?strict=false"; 
     } 

     public void Delete<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class, new() { 
      //not efficient, NoRM should do this in a way that sends a single command to MongoDB. 
      var items = All<T>().Where(expression); 
      foreach (T item in items) { 
       Delete(item); 
      } 
     } 

     public void Delete<T>(T item) where T : class, new() { 
      using(var db = Mongo.Create(_connectionString)) 
      { 
       db.Database.GetCollection<T>().Delete(item); 
      } 
     } 

     public void DeleteAll<T>() where T : class, new() { 
      using(var db = Mongo.Create(_connectionString)) 
      { 
       db.Database.DropCollection(typeof(T).Name); 
      } 
     } 

     public T Single<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class, new() { 
      T retval = default(T); 
      using(var db = Mongo.Create(_connectionString)) 
      { 
       retval = db.GetCollection<T>().AsQueryable() 
         .Where(expression).SingleOrDefault(); 
      } 
      return retval; 
     } 

     public IQueryable<T> All<T>() where T : class, new() { 
      //don't keep this longer than you need it. 
      var db = Mongo.Create(_connectionString); 
      return db.GetCollection<T>().AsQueryable(); 
     } 

     public void Add<T>(T item) where T : class, new() { 
      using(var db = Mongo.Create(_connectionString)) 
      { 
       db.GetCollection<T>().Insert(item); 
      } 
     } 

     public void Add<T>(IEnumerable<T> items) where T : class, new() { 
      //this is WAY faster than doing single inserts. 
      using(var db = Mongo.Create(_connectionString)) 
      { 
       db.GetCollection<T>().Insert(items); 
      } 
     } 

     public void Update<T>(T item) where T : class, new() { 
      using(var db = Mongo.Create(_connectionString)) 
      { 
       db.GetCollection<T>().UpdateOne(item, item); 
      } 
     } 

     //this is just some sugar if you need it. 
     public T MapReduce<T>(string map, string reduce) { 
      T result = default(T); 
      using(var db = Mongo.Create(_connectionString)) 
      { 
      var mr = db.Database.CreateMapReduce(); 
      MapReduceResponse response = 
       mr.Execute(new MapReduceOptions(typeof(T).Name) { 
        Map = map, 
        Reduce = reduce 
       }); 
      MongoCollection<MapReduceResult<T>> coll = response.GetCollection<MapReduceResult<T>>(); 
      MapReduceResult<T> r = coll.Find().FirstOrDefault(); 
      result = r.Value; 
      } 
      return result; 
     } 

     public void Dispose() { 
      _server.Dispose(); 
     } 
    } 
+0

Как насчет инъекции зависимостей через каркас (или самонаписанный)? – DrColossos

+0

@DrColossos, На самом деле я использую структуру под названием Castle.Windsor. Я видел некоторый код Rob Conery, имитирующий сеансы, но он подключается к БД при каждом создании/обновлении/удалении/поиске, и я не делаю, если это оптимально. –

ответ

17

Не беспокойтесь об открытии и закрытии соединений. Драйвер MongoDB C# поддерживает внутренний пул соединений, поэтому вы не будете страдать от накладных расходов при открытии и закрытии реальных подключений каждый раз, когда вы создаете новый объект MongoServer.

Вы можете создать интерфейс репозитория, который предоставляет вашу логику данных, и построить реализацию MongoDB, которая вводится там, где это необходимо. Таким образом, специальный код подключения MongoDB удаляется от вашего приложения, которое видит только IRepository.

Будьте осторожны, пытаясь реализовать шаблон типа единицы работы с MongoDB. В отличие от SQL Server, вы не можете заручиться несколькими запросами в транзакции, которая может быть отброшена, если не удастся.

Для простого примера шаблона репозитория, который имеет реализации MongoDB, SQL Server и JSON, проверьте NBlog storage code. Он использует Autofac IoC для ввода конкретных репозиториев в приложение ASP.NET MVC.

+0

Крис, спасибо за ответ. Я просмотрел ваш код для репозитория. В конструкторе вы создаете и сохраняете на протяжении всего жизненного цикла экземпляр MongoServer. Например, ваш тестовый код модуля для IoC должен решить какой-то репозиторий. Это означает, что ваш сервер сборки может выйти из строя, потому что в нем нет процесса mongos. И даже концептуально это не кажется ОК –

+0

@Hohhi это интеграционные тесты, а не модульные тесты, они ожидают (и требуют) процесса mongod, выполняемого локально для передачи. –

+0

Какое подходящее место для применения индекса? –

0

Если вы заинтересованы в реализации подобной Роб Коннери и NBlog код для хранения, но используя драйвер MongoDB Csharp 2.0 (то есть асинхронный), вы можете посмотреть по адресу:

https://github.com/alexandre-spieser/mongodb-generic-repository

Вы можете написать пользовательский репозиторий, наследующий от BaseMongoRepository.

public interface ITestRepository : IBaseMongoRepository 
{ 
    void DropTestCollection<TDocument>(); 
    void DropTestCollection<TDocument>(string partitionKey); 
} 

public class TestRepository : BaseMongoRepository, ITestRepository 
{ 
    public TestRepository(string connectionString, string databaseName) : base(connectionString, databaseName) 
    { 
    } 

    public void DropTestCollection<TDocument>() 
    { 
     MongoDbContext.DropCollection<TDocument>(); 
    } 

    public void DropTestCollection<TDocument>(string partitionKey) 
    { 
     MongoDbContext.DropCollection<TDocument>(partitionKey); 
    } 
}