Я писал об использовании MongoDb для хранения журналов log4net , но у исходного приложения есть небольшая проблема: он хранит BsonDocument внутри MongoCollection и не использует какой-либо объект C #, а когда приходит время запрашивать данные, вы не можете использовать новый LINQ. Поставщик Mongo, который включен в новейшие драйверы C # (1.4 ), потому что данные полностью нетипизированы.
Обычно это не большая проблема, потому что вы можете запросить коллекцию, используя простой JSON-подобный запрос , но если вы привыкли к стандартному поставщику LINQ , возможно, вы немного потеряете в том, как создать запрос JSON для извлечения данных. тебе нужно. Вот сценарий, который мне нужно решить : у меня есть дурацкая Winform, которая способна показать некоторую информацию из стандартной базы данных Log4Net Sql (с помощью adonetappender), и я хочу иметь возможность использовать тот же интерфейс для загрузки данных из базы данных mongo , Прежде всего мне нужно загрузить все различные значения для уровня свойства и loggerName, потому что у меня есть комбо, где пользователь может фильтровать по серьезности (ERROR, WARN, INFO и т. д.) и список флажков, используемых для фильтрации по loggerName. К счастью, Монго предлагает такую функциональность из коробки.
var db = server.GetDatabase(bsDatabase.Current as string); var collection = db.GetCollection(bsCollections.CurrencyManager.Current as string); var allLevel = collection.Distinct("level");
Код действительно прост: я создаю соединение с базой данных и затем извлекаю ссылку на коллекцию, которая была выбрана в интерфейсе от пользователя, затем я использую метод Distinct () объекта MongoCollection, передавая имя нужного вам свойства и Монго дал вам отдельный список каждого значения этого свойства . Это позволяет мне заполнить пользовательский интерфейс несколькими строками кода.
Теперь пришло время подумать, как создать запрос для извлечения всего документа с определенным уровнем ведения журнала и принадлежности к списку возможных loggerName . В моем программном обеспечении я обычно использую Castle Log4net Integration, это означает, что свойство loggerName равно имени класса, который выдает журнал, и обычно пользователь хочет видеть журналы, принадлежащие одному или нескольким классам, что-то вроде: все ОШИБКИ из класса или ОШИБКА . Mongo имеет вспомогательный класс QueryBuilder, который облегчает создание такого запроса с небольшой помощью intellisense и без необходимости пачкать руки напрямую с помощью JSON
var query = Query.And( Query.EQ("level", "ERROR"), Query.Or( Query.EQ("loggerName", "classa"), Query.EQ("loggerName", "classb") ) );
Теперь мне нужно сделать этот код динамическим, потому что мне нужно создать запрос, который извлекает журналы, принадлежащие неизвестному номеру loggerName , например «classa», «classb» и «classc» , потому что пользовательский интерфейс содержит CheckBoxList каждого loggerName представить в базу данных, и пользователь может выбрать любое количество элементов для поиска, поэтому мне нужно динамически создавать список условий для создания динамического запроса.
List<IMongoQuery> mainQueries = new List<IMongoQuery>(); if (!String.IsNullOrWhiteSpace(cmbLevel.Text)) { mainQueries.Add(Query.EQ("level", cmbLevel.Text)); } if (cbList.CheckedIndices.Count > 0) { List<IMongoQuery> listOfLoggerNameQueries = new List<IMongoQuery>(); foreach (Int32 index in cbList.CheckedIndices) { String value = cbList.Items[index].ToString(); listOfLoggerNameQueries.Add(Query.EQ("loggerName", value)); } mainQueries.Add(Query.Or(listOfLoggerNameQueries.ToArray())); } var finalQuery = Query.And(mainQueries.ToArray());
Код действительно прост, потому что он просто создает список объекта IMongoQuery, который содержит все условия первого уровня, которые будут объединены в последней инструкции с помощником Query.And () . Поскольку я могу выбрать более одного loggerName из checkboxList, я могу просто перебрать все CheckedIndices и создать условие Query.EQ («loggerName», значение) для каждого проверенного имени в пользовательском интерфейсе, тогда я могу объединить все эти условия с Query. Или () для создания одного IMongoQuery, который добавляется в основной список .
После того, как у вас есть запрос, вы можете использовать для получения записей.
var cursor = collection.Find(finalQuery); Int32 limit; if (!Int32.TryParse(txtLogNum.Text, out limit)) { limit = 50; } cursor.SetFields("level", "loggerName", "message", "exception", "customproperties", "timestamp"); cursor.SetSortOrder(SortBy.Descending("timestamp")); cursor.Limit = limit;
Наконец, метод MongoCollection.Find () возвращает курсор, который на самом деле не содержит каких-либо данных, теперь вы можете добавить сортировку, разбиение на страницы и указать все свойства, которые вы хотите вернуть непосредственно в curso, и когда вы выполняете все элементы с foreach данные будут получены из базы данных. Это действительно похоже на запрос LINQ, где данные не извлекаются, если вы вызываете Where (), Select () и т. Д., Но только когда вы повторяете запрос или вызываете не отложенный оператор, такой как List ().
С помощью этого простого кода я могу создать простую форму для быстрой визуализации всех журналов, хранящихся в базе данных Mongo, даже если приложение хранит нетипизированные объекты.