Просто предупреждение, прежде чем читать это. В настоящее время я не рекомендую использовать этот проект. Может быть, в будущем вы могли бы использовать его, но сейчас я думаю, что это просто классное доказательство концепции или что-то удобное для быстрого прототипирования.
На прошлой неделе у меня была дискуссия с @PrabirShrestha в JabbR об API для MongoDB, чтобы достичь чего-то вроде:
db.posts.insert({title: ''first''}, function(err, doc){})
Пример из: http://alexeypetrushin.github.com/mongo-lite/docs/index.html
Или же
_db.Users.Insert(Name: "Steve", Age: 50);
Пример из: http://simplefx.org/simpledata/docs/pages/Modify/AddingData.html
Глядя на синтаксис и думая о C # DynamicObject, я решил посмотреть, что я могу придумать. Я потратил лучшую часть следующих 2-3 часов, возясь в Visual Studio, общаясь и возвращаясь к ней, и придумал эту суть.
https://gist.github.com/3798206
Я в основном придумал действительно грубый рабочий прототип. В настоящее время он мало что делает, но он позволяет вам обернуть IDocumentSession RavenDB в то, что я назвал «DynamicSession», внутри этого класса — это вложенный класс Chainer, который / будет отвечать за все метод / свойство цепочка, которая происходит, чтобы сделать API полностью динамичным.
Вместо того, чтобы создавать нормальный сеанс, вы создаете OpenDynamicSession. Все, что он действительно делает, это избавляет вас от необходимости писать два оператора using.
public static class DynamicSessionExtension { public static DynamicSession OpenDynamicSession(this IDocumentStore store) { return new DynamicSession(store.OpenSession()); } }
Он просто открывает обычный сеанс RavenDB и передает его.
using (dynamic session = store.OpenDynamicSession()) { session.Bananas.insert(new { Colour = "Yellow", Bunch = 14 }, 1); session.SaveChanges(); }
Этот фрагмент кода позволяет вызвать динамическое свойство «Бананы», это относится к коллекции. (Предупреждение: RavenDB чувствителен к регистру, я не знаю, что делать в этом сценарии)
Затем следующая часть — это команда, которая принимает анонимный тип в качестве первого параметра и идентификатор в качестве второго параметра. Я еще не выяснил, как позволить RavenDB генерировать Id, не уверен, возможно ли это, но посмотрим ?
using (dynamic session = documentStore.OpenDynamicSession()) { dynamic post1 = session.Posts.load(123); Console.WriteLine(post1.Name); }
По этому сценарию. Я просто вызываю Load, которая возвращает динамический тип, который вы можете вызвать все свойства, которые вы хотите отключить.
Я не создал POCOs ?
Пока что мне пришлось преодолеть два препятствия:
Названия коллекций
RavenDB использует метаданные для помещения документа в коллекцию. При вставке RavenDB не знает, что такое коллекция, так как нет имени класса, из которого она может быть выведена. Чтобы обойти это, я решил, что когда у вас есть объект из RavenDB, вы можете получить доступ к метаданным. Это делается так:
var metadata = Session.Advanced.GetMetadataFor(objectToStore);
Это позволяет мне использовать коллекцию с именем, к которой обращались ранее, чтобы назначить ее для имени объекта RavenDB:
metadata["Raven-Entity-Name"] = CollectionName;
Это позволяет сгруппировать все документы и поместить их в нужную коллекцию.
Как я упоминал ранее, RavenDB чувствителен к регистру, поэтому, если вы вставите:
session.Posts
затем session.posts
они будут храниться в двух разных коллекциях (эта особенность RavenDB мне лично не нравится, да, я люблю RavenDB, но даже в ней есть вещи, которые мне не нравятся, нет ничего идеального)
Мета-данные подводят меня ко второму вопросу …
Тип CLR
RavenDB сохраняет тип CLR в метаданных, поэтому при использовании Newtonsoft.Json он может использовать эти данные для создания нужного объекта и его возврата.
Проблема со вставкой анонимных типов заключается в том, что нет реальной информации о типе CLR для сохранения, поэтому в итоге все ваши сущности сохраняются в виде:
«Raven-Clr-Type»: «<> f__AnonymousType11 [[System.String, mscorlib]], Raven.DynamicSession.TestConsole»
Это мешает нам делать:
using (var session = store.OpenSession()) { result = session.Load<Post>("posts/123"); }
Так как RavenDB не может преобразовать тип. Это, оказывается, еще одна довольно раздражающая вещь, которую я нахожу в RavenDB. При использовании Index вы можете избежать их приведения с помощью As или вызова AsProjection, когда тип отличается с точки зрения свойств. Но при доступе к конкретному документу через идентификатор вы не можете вернуть свой собственный указанный тип, независимо от того, совпадают ли свойства.
Я обошел это с помощью соглашения, прежде всего я сохраняю МОЮ СОБСТВЕННУЮ информацию в метаданных ?
metadata[DynamicClrTypePlaceHolder] = CollectionName;
Который сохраняется в документе так:
«Raven.DynamicSession.DynamicClrTypePlaceHolder»: «Люди»
Тогда я настраиваю магазин с соглашением.
store.Conventions.FindClrType = (id, doc, metadata) => { var clrType = metadata.Value<string>(DynamicSession.DynamicClrTypePlaceHolder); if (clrType.Equals(clrPlaceHolder, StringComparison.OrdinalIgnoreCase)) return type.FullName; return metadata.Value<string>(Constants.RavenClrType); };
Он проверяет, соответствует ли он, и затем возвращает тип, который я определяю, а не то, что установил RavenDB.
Вот рабочий тест: https://github.com/phillip-haydon/Raven.DynamicSession/blob/master/src/Raven.DynamicSession.Tests/QueryGetFixture.cs
На данный момент это соглашение жестко запрограммировано для обработки одного типа, но я планирую расширить его и включить в некоторую конфигурацию, которую вы можете настроить один раз.
Этот проект можно найти на GitHub:
https://github.com/phillip-haydon/Raven.DynamicSession
Я планирую и дальше расширять его и пытаться охватить все основы RavenDB, некоторые из которых не легки, а хаки, которые я должен применить для обработки POCO, заставляют меня чувствовать, что это никогда не может быть использовано в реальном мире.
Но в качестве прототипа я думаю, что это круто!
Интересно услышать чей-либо отзыв. Это все еще меняется, я потратил около 4 часов, играя с этим. Так что там не было много работы. У меня была некоторая помощь от StackOverflow, когда я хотел узнать, возможно ли связать динамические методы, но оказывается, что вам нужно создавать новые объекты с информацией и объединять их в цепочку.
http://stackoverflow.com/questions/12634250/possible-to-get-chained-value-of-dynamicobject