Strongly-typed C# Document wrapper

3184 views
Skip to first unread message

GWBasic

unread,
Feb 23, 2010, 2:18:09 AM2/23/10
to mongodb-csharp, mongod...@googlegroups.com
I'm pleased to announce a strongly-typed C# Document wrapper. It's
only available as source:
http://bitbucket.org/gwbasic/mongodb.emitter/
http://bitbucket.org/gwbasic/mongodb.emitter/get/6e2f075700a7.zip

I've been using my strongly typed Document wrapper for about a month
without any major issues.

In summary, the Document wrapper works with Sam's unmodified drivers.
You (the programmer) define interfaces with properties. Then, to get
an object that implements the interface, pass a document to
WrapperFactory's New method to get back an object that implements your
interface.

The system is optimized for scenarios where you will *mostly* access
your data in a strongly-typed manner, but still occasionally need to
access the underlying Document object. (This is useful for
applications that need to support user-defined schemas.) My wrapper
will gracefully let you access and manipulate the underlying Document
object. You can also have a strongly-typed Document property; if your
application needs that kind of flexibility.

The limitations of the system is that you must run with sufficient
privileges to be able to support runtime-generated code.

-------

Anyway, here's my README that goes into more detail regarding usage.

-------

MongoDB.Emitter: Emits strongly-typed wrappers for
MongoDB.Driver.Document objects
http://objectcloud.com
(C) 2010 Andrew Rondeau
MIT License (Open Source)

A module for use when working with MongoDB and C#. MongoDB.Emitter
takes interfaces, and at runtime, generates
types that wrap Document objects from MongoDB.Driver. This allows for
strongly-typed use of Document objects,
yet provides flexibility when handling different data types. Due to
run-time code generation; use of this
library requires high privilages. It may not work when running in a
tightly-controlled sandbox situation.

***
NOTE: You must provide your own copy of MongoDB.Driver.dll in the
same location as this README in order to build!!!
***

Usage:
----------------

(For an exhaustive set of samples, see MongoDB.Emitter.Test)

// Use the MongoDB.Emitter namespace
using MongoDB.Emitter;

...

// Declare interfaces. You can use basic primitives, the ArrayWrapper
class to wrap "arrays", and the
// DictionaryWrapper class to wrap "dictionaries"
public interface IParent : IDocumentWrapper
{
int Value { get; set; }
bool Value_IsPresent { get; set; }
ArrayWrapper<long> Array { get; set; }
DictionaryWrapper<string> NVPs { get; set; }
IChild Child { get; set; }
}

public interface IChild : IDocumentWrapper
{
int SubValue { get; set; }
}

// Code:
IParent parent = WrapperFactory.Instance.New<IParent>();
parent.Value_IsPresent // returns false
parent.Value = 5;
parent.Value_IsPresent // returns true now that Value is set
parent.Array = WrapperFactory.Instance.NewArrayWrapper<long>();
parent.Array.Add(234);
parent.NVPs = WrapperFactory.Instance.NewDictionaryWrapper<string>();
parent.NVPs["foo"] = bar;
parent.Child = WrapperFactory.Instance.New<IChild>();
parent.Child.SubValue = 33;

// You can still access and manipulate underlying Document objects
((Document)parent.Document["Child"])["SubValue"] // returns 33
((Document)parent.Document["Child"])["SubValue"] = 44;
parent.Child.SubValue // returns 44

Document d = MongoCollection.FindOne(...);
parent = WrapperFactory.Instance.New<IParent>(d);

Note: Emitted types check DateTime's Kind property, and will throw an
exception for non-UTC types.
Note: It is possible to have properties that are of type Document.

Philosophy:
----------------

The vast variety of Object/Relational mappers and serialization
libraries for C# (and Java) indicates that there is
no perfect approach to converting serialized persistant data into
objects or structs easily consumed by C# (and
Java.) All libraries, code generators, and design patterns have their
strengths and weaknesses.

MongoDB.Emitter attempts to be a "good enough" approach for
applications that anticipate some flexibility in their
data, but need strong types for developer convenience. The goal is to
allow rapid creation of strong types to assist
in working with Mongo Documents; but to stay out of the way when
manipulating the Document object directly is best.

Another goal is to be simple to learn; more complicated mappers and
serialzers can sometimes make simple data
representations significantly more difficult to implement due to their
learning curve. Sometimes it really is better
for a mapper to expose the lower-layers when they present a better
API. Objects of emitted types keep the "official"
version of data in Document objects; thus it's possible to mix use of
emitted types and document objects.

The Emitter only works with types that have no methods. This is
because mapping into business logic types can create
logical inconsistancies; especially if business logic requires
semantics or structures that the emitter is unable to
anticipate. It is reccomended to restrict using emitted types to
layers close to the database.

Advanced Usage and Customization:
----------------

Using the singleton instance of WrapperFactory is strongly reccomended
unless special circumstances dictate that a
program have a small set of WrapperFactory objects. This is because
the .Net framework never garbage collects
generated code. Each instance of WrapperFactory caches and re-uses
code it generates for each type. If the
programmer create a new instance of WrapperFactory each time a query
is run; the program will run out of memroy at
some point, even if the WrapperFactories are garbage collected.

The Emitter doesn't support all types. You can register custom
converters by calling the
RegisterConverterDelegates<T> method. It's also possible to build in
new types by adding a call in
WrapperFactory_DefaultConversionDelegates.cs.

Schema evolution is supported by registering a delegate that will be
called prior to wrapping a Document object.
These are stored in the Evolvers dictionary.

Wrapped objects are not thread-safe, but WrapperFactory is intended to
be thread-safe. (Thread-safety is untested.)

Performance:
----------------

Due to the underlying use of a Document object, using an Emitted
object will incur a minimal performance cost over
the Document object. Each instance of WrapperFactory stores generated
types for re-use, so the act of generating a
type for re-use incurs minimal performence overhead.

The wrappers will perform best with properties that map to a
primitives. Dictionaries, and other wrapped objects
have some minor logic that's used to ensure that the wrapper keeps
pointing at the wrapped object. Accessing an
ArrayWrapper for the first time will copy and replace the default
value. This is due to unpredictable behavior
in MongoDB.Driver.

ArrayWrappers and Dictionaries that contain a wrapper type, instead of
a primitive, do not cache the generated wrapper
object. They will always re-wrap on each get.

Overall, performance of emitted types is limited by the symbolic
nature of the Document itself; accessing values
by a symbolic string instead of by strongly-typed property incurs a
marginal CPU cost. In general, the emitter
shouldn't add noticable overhead to the Document API.

For situations where "every clock cycle is sacred," the emitter, and
thus the Document object's API, will be too
slow. In these situations, it is best to figure out how to expose a
BSON reader and populate strongly-typed objects
in an optimized manner.

Default Supported Types:
----------------

long / long?
int / int?
double / double?
bool / bool?
DateTime / DateTime? (Note, throws exception if setting a DateTime
that isn't UTC)
string
byte[]
Guid / Guid? (Saved as a UUID)
Document
object

Dwight Merriman

unread,
Feb 23, 2010, 10:23:53 AM2/23/10
to mongod...@googlegroups.com
i added it to the c# page on the wiki


--
You received this message because you are subscribed to the Google Groups "mongodb-user" group.
To post to this group, send email to mongod...@googlegroups.com.
To unsubscribe from this group, send email to mongodb-user...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/mongodb-user?hl=en.


Reply all
Reply to author
Forward
0 new messages