Hugoware

The product of a web developer with a little too much caffeine

Archive for March 2010

Static Constructors — What? What???

with 3 comments

The best days of the year fall on the days that I learn something so completely new and useful in C# that I literally rush out to find a good place to use it. It has happened several times over the years, for example when discovered coalescing operators and ternary operators (not limited to C# but you get it).

Well, that great day has arrived again!

So look at this code – I write code like this fairly often…

/// <summary>
/// Manages resources or something...
/// </summary>
public class WebResources {

    /// <summary>
    /// Returns a list of resource files
    /// </summary>
    public static IEnumerable<Resource> ResourceList {
        get {

            //check for the resource
            if (WebResources._ResourceList == null) {
                WebResources._ResourceList = WebResources._LoadResources();
            }

            //return the resources
            return WebResources._ResourceList;

        }
    }
    private static IEnumerable<Resource> _ResourceList;

    //snip...
}

The goal of the code was to create a static property that only allowed for you to get the values. I’d always end up having to write extra code to check for the value then load it if it wasn’t ready yet. This threw an extra if statement into every request.

While a single extra comparison isn’t really going to be a big deal I still felt the code hurt the clarity of the class. Properties are rarely supposed to do anything. Talking to external resources and loading files… not really a good idea. Not only that, it placed setup code in the middle of the class instead up mixed in with the constructors where it really belongs.

However, behold coding greatness…

/// <summary>
/// Manages resources or something...
/// </summary>
public class WebResources {

    //Oh yeah!!
    static WebResources() {
        WebResources.ResourceList = WebResources._LoadResources();
    }

    /// <summary>
    /// Returns a list of resource files
    /// </summary>
    public static IEnumerable<string> ResourceList { get; private set; }

    //snip...
}

You see that? A static constructor! It feels like I’ve discovered something rare like a unicorn or something… but in the form of a code snippet.

Now you should note that this code doesn’t run as soon as your application starts. Instead, it is fired off the first time you access the class. It is important to keep that in mind – Imagine if you have a long running process and it kicks off in the middle of your programming running. Here are a few examples of what I mean…

Examples where the static constructor will NOT run


//checking the type against another object
someObject is WebResources

//loading the Type data for the class
typeof(WebResources)

//trying to cast the value
WebResources temp = someObject as WebResources;

Examples where the static constructor WILL run


//creating an instance
WebResources resources = new WebResources();

//accessing a property
IEnumerable<Resource> resources = WebResources.Resources;

//using an inheriting class
public class SuperWebResources : WebResources { }
IEnumerable<Resource> resources = SuperWebResources.Resources;

Its too bad I’m only just now finding out about this little gem of code. I’m sure some of you smart devs could have clued me in about this a long time ago. I guess that’s just the way it goes being the only programmer in your company for this long… Oh well… 😐

Anyways – Helpful? Known it all along (And if so, why have you been holding out on me!)

Written by hugoware

March 28, 2010 at 9:18 pm

Posted in General Discussions

Tagged with , , ,

A Personable Form Of Impersonation

leave a comment »

Have you tried impersonating a different user when executing code in your ASP.NET sites? Yeah, you can use a bunch of options in the web and machine config files but those might not give you enough control. Whats more you still have to contend with the way IIS handles credentials with the worker process then connecting user… er… or was that connecting user then… ah… never mind…

Executing a block of code with a specific set of credentials is a very handy way to have precise control over your application. Recently, I was working on copying files from an ASP.NET website to a remote UNC path. I needed to perform the transfer using different credentials for different servers… yeah, it was ugly but required…

Below is the code I finally ended up with…

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text.RegularExpressions;

namespace Samples {

    /// <summary>
    /// Allows you to execute code with an alternate set of credentials
    /// </summary>
    public class ImpersonationContext : IDisposable {

        #region Imported Methods

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool CloseHandle(IntPtr handle);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool RevertToSelf();

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int DuplicateToken(
            IntPtr token, 
            int impersonationLevel, 
            ref IntPtr newToken
            );

        [DllImport("advapi32.dll")]
        private static extern int LogonUserA(
            string username,
            string domain,
            string password,
            int logonType,
            int logonProvider,
            ref IntPtr token
            );

        #endregion

        #region Constants

        private const int INTERACTIVE_LOGON = 2;
        private const int DEFAULT_PROVIDER = 0;

        private const string REGEX_GROUP_USERNAME = "username";
        private const string REGEX_GROUP_DOMAIN = "domain";
        private const string REGEX_EXTRACT_USER_INFO =
            @"^(?<domain>[^\\]+)\\(?<username>.*)$|^(?<username>[^@]+)@(?<domain>.*)$";

        private const string EXCEPTION_COULD_NOT_IMPERSONATE = 
            "Could not impersonate user '{0}'.";
        private const string EXCEPTION_COULD_NOT_PARSE_USERNAME = 
            "Cannot determine username and domain from '{0}'";

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new Impersonation context
        /// </summary>
        public ImpersonationContext(string fullUsername, string password) {
            this.SetCredentials(fullUsername, password);
        }

        /// <summary>
        /// Creates a new Impersonation context
        /// </summary>
        public ImpersonationContext(string username, string domain, string password) {
            this.SetCredentials(username, domain, password);
        }

        #endregion

        #region Static Creation

        /// <summary>
        /// Executes a set of code using the credentials provided
        /// </summary>
        public static void Execute(string fullUsername, string password, Action action) {
            using (ImpersonationContext context = new ImpersonationContext(fullUsername, password)) {
                context.Execute(action);
            }
        }

        /// <summary>
        /// Executes a set of code using the credentials provided
        /// </summary>
        public static void Execute(string username, string domain, string password, Action action) {
            using (ImpersonationContext context = new ImpersonationContext(username, domain, password)) {
                context.Execute(action);
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// The username for this connection
        /// </summary>
        public string Username { get; private set; }

        /// <summary>
        /// The domain name for this user
        /// </summary>
        public string Domain { get; private set; }

        /// <summary>
        /// The identity of the executing account
        /// </summary>
        public WindowsIdentity Identity { get; private set; }

        //connection details
        private string _Password;
        private WindowsImpersonationContext _Context;

        #endregion

        #region Private Methods

        /// <summary>
        /// Begins to impersonate the provided credentials
        /// </summary>
        public bool BeginImpersonation() {

            //create the token containers
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;

            if (ImpersonationContext.RevertToSelf()) {

                //attempt the login
                int success = ImpersonationContext.LogonUserA(
                    this.Username,
                    this.Domain,
                    this._Password,
                    INTERACTIVE_LOGON,
                    DEFAULT_PROVIDER,
                    ref token
                    );

                //if this worked, perform the impersonation
                if (success != 0) {

                    int duplicate = ImpersonationContext.DuplicateToken(token, 2, ref tokenDuplicate);
                    if (duplicate != 0) {

                        //assign the identity to use
                        //this.Identity = new WindowsIdentity(tokenDuplicate);
                        this._Context = WindowsIdentity.Impersonate(tokenDuplicate);
                        if (this._Context != null) {
                            ImpersonationContext.CloseHandle(token);
                            ImpersonationContext.CloseHandle(tokenDuplicate);
                            return true;
                        }

                    }
                }
            }

            //close the tokens if required
            if (token != IntPtr.Zero) { ImpersonationContext.CloseHandle(token); }
            if (tokenDuplicate != IntPtr.Zero) { ImpersonationContext.CloseHandle(tokenDuplicate); }

            //return this failed
            return false;
        }

        /// <summary>
        /// Ends impersonating the current request
        /// </summary>
        public void EndImpersonation() {
            if (this._Context is WindowsImpersonationContext) { this._Context.Undo(); }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Accepts a full domain and assigns it for this connection
        /// </summary>
        public void SetCredentials(string fullUsername, string password) {

            //extract the user information
            Match user = Regex.Match(fullUsername, REGEX_EXTRACT_USER_INFO);
            if (!user.Success) {
                string message = string.Format(EXCEPTION_COULD_NOT_PARSE_USERNAME, fullUsername);
                throw new ArgumentException(message);
            }

            //extract the values
            string username = user.Groups[REGEX_GROUP_USERNAME].Value;
            string domain = user.Groups[REGEX_GROUP_DOMAIN].Value;

            //update the credentials
            this.SetCredentials(username, domain, password);

        }

        /// <summary>
        /// Changes the credentials for this connection to use
        /// </summary>
        public void SetCredentials(string username, string domain, string password) {
            this.Username = username;
            this.Domain = domain;
            this._Password = password;
        }

        /// <summary>
        /// Executes the action using the credentials provided
        /// </summary>
        public void Execute(Action action) {

            //perform the requested action
            if (this.BeginImpersonation()) {
                try {
                    action();
                }
                finally {
                    this.EndImpersonation();
                }
            }
            //since this couldn't login, give up
            else {
                string message = string.Format(EXCEPTION_COULD_NOT_IMPERSONATE, this.Username);
                throw new OperationCanceledException(message);
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Performs any cleanup work
        /// </summary>
        public void Dispose() {
            this.EndImpersonation();
            if (this._Context is WindowsImpersonationContext) { this._Context.Dispose(); }
        }

        #endregion

    }

}

Nothing like a lot of code to help fill in the blanks of a blog post, huh?

After reading a dozen blog post, tutorials and wikis, this is what I finally came up with – a nice simple way to execute a block of code without needing to worry about a lot of setup options. How about a few examples?

//who is running the code now
string who = WindowsIdentity.GetCurrent().Name;

//the credentials to use 
string username = "domain\\hugoware";
string password = "passw0rd";

//create a context inside of a using statement
//or simply create it and dispose it yourself
using(ImpersonationContext context = new ImpersonationContext(username, password)) {
    
    //execute using a anonymous method
    context.Execute(() => {
        who = WindowsIdentity.GetCurrent().Name;
    });

    //or manage it yourself
    context.BeginImpersonation();
    who = WindowsIdentity.GetCurrent().Name;
    context.EndImpersonation(); //optional if inside of a 'using'

}

//or use static methods
ImpersonationContext.Execute(username, password, () => {
    who = WindowsIdentity.GetCurrent().Name;
});

At this point I’m not exactly sure what this means for code that isn’t exactly straight forward, for example Threading or executing code when the credentials don’t have any access to the machine. I’ll follow up later as I find out more.

Written by hugoware

March 25, 2010 at 9:38 pm

Performing Updates With CSMongo

with 7 comments

I got some good questions earlier today about how to perform updates on records in CSMongo. Rather than doing a short answer in a comment I figured a full post about it might be more helpful. While playing with this I made some changes to the code so if you have a previous version then you’re going to want to update to the newest version.

CSMongo has a couple ways available to update records right now. One is after you have made a selection from a database and the other is a query sent to the database.

Update After Selection

If you’ve used LINQ to SQL before then you know that you can load database objects into memory, modify them and then submit the changes and like magic your records are updated… maybe not *real* magic but I was still impressed the first time I saw it…

CSMongo allows for a similar approach when performing updates on documents loaded from the database. For example, here is how we could load a set of users, modify them and then issue back our changes.

//connect to the database
using (MongoDatabase database = new MongoDatabase(connectionString)) {

    //load a set of records
    MongoCollection collection = database.GetCollection("drivers");
    
    //select a set of records
    var drivers = collection.Find()
        .Greater("age", 16)
        .Select();

    //make some changes
    drivers.Apply(new {
        canDrive = true
    });

    //submit the changes
    collection.SubmitChanges();

}

This code load in a set of records and saves their reference to the collection (which is also managed by the MongoDatabase class in case you were wondering). This allows you to make changes to your object in multiple places and then call SubmitChanges to apply everything you’ve done.

When the record is first loaded a hash is created of the object which is used to check for changes which means that if you don’t change anything, or if values are set but not actually different then the update request is never sent.

It is also important to realize that MongoCollection.SubmitChanges() only sends updates for the collection that is called on whereas MongoDatabase.SubmitChanges() checks all of the collections that have been loaded and attempts to apply their changes. This is actually one of the advantages to using the MongoDatabase to create instances of your MongoCollection since it can automatically handle checking for changes.

In this last example we don’t actually use any of the information in the record which makes loading it into memory first rather pointless which leads us into the next type of update.

Immediate Updates

Sometimes when you want to change records you don’t want to have to load them first. Sometimes you simply want to perform and update for a bunch of matching records that meet a certain condition. In that instance you can issue an update immediately from a MongoQuery.

The example we used above is a good example where sending an update would be better than loading the records first. There isn’t a lot that changes but what happens in the background is much different.

//connect to the database
using (MongoDatabase database = new MongoDatabase(connectionString)) {

    //issue the query directly from the database level
    database.From("drivers")
        .Greater("age", 16)
        .Set(new {
            canDrive = true
        });

}

You can also perform an update directly from the MongoCollection by using the Find() command, which starts a MongoQuery that can be used for the update.

You may notice that this example doesn’t have a call to SubmitChanges() in it — That’s because when I say immediate then by golly, I mean right away! In fact, if I remember correctly ever command on the MongoDatabase level is evaluated immediately.

Anyways, CSMongo is still early in development so if anyone has some additional ideas how to handle these updates then I’m certainly interested in what you think.

Written by hugoware

March 17, 2010 at 10:04 pm

MVC2 As A “WebOS”

with 3 comments

Just some random thoughts for today…

I’ve been checking out MVC2, particularly the ‘Areas’ feature, which allows you to create subsections of controllers, actions and views. This concept allows for you to isolate tasks for an application without needing to create multiple projects and shared libraries.

It sounds like it has a lot of potential which got me to thinking – Could something like this be used to create separate, ‘installable’ applications to use within a ‘web-OS’ type framework?

The general idea would include creating a framework that allow controllers to be installed as ‘applications’ within the program. Using a base type for ‘Application’ Controllers, you could effectively have a sort of ‘programs list’ that a user could use for programs.

I suppose you could just as easily create Widgets or ‘Task Bar’ applications — really, just about anything that you could think of. By default, the ‘MvcOS’ could use jQuery as the core which would allow for thousands of existing plugins to fit right in.

Maybe this isn’t that great of an idea, maybe it is too much like existing projects — not really sure. Thoughts?

Written by hugoware

March 15, 2010 at 12:17 am

Copyin’ and-a Pastin’

with 2 comments

How often do you use Copy and Paste in your code — because you really shouldn’t…

I’m not really a very opinionated person when it comes to software development but this is one point I tend to be adamant about with other developers. What you ought to be doing is Cutting and Pasting your code.

If I happen to catch a new developer copying some code I normally lead off with that sentence to which I get that raised eyebrow, “what is this dude talking about?” look but it is always a fun discussion.

You’re probably already realize the difference between the two, duplication as opposed to refactoring, but so far every new developer I’ve encountered hadn’t even heard the term before. Maybe it is just a matter of coincidence but I doubt it.

Normally, I show a simple example of how by refactoring you can limit errors and reduce how much work you have to do. A lot of you are going to see the problem with this right away…

//a user settings file
public class UserSettings {

    //holds the settings for the user
    public XDocument Settings { get; set; }

    //the users font color
    public string FontColor {
        get {
            if (this.Settings == null) {
                this.Setting = XDocument.Load(@"c:\settings.xml");
            }
            return this.Settings.Root.Element("fontColor").Value;
        }
        set {
            if (this.Settings == null) {
                this.Settings = XDocument.Load(@"c:\settings.xml");
            }
            this.Settings.Root.Element("fontColor").Value = value;
            this.Settings.Save(@"c:\settings.xml");
        }
    }

    //the size of the font
    public int FontSize {
        get {
            if (this.Settings == null) {
                this.Setting = XDocument.Load(@"c:\settings.xml");
            }
            int size = 0;
            int.TryParse(this.Settings.Root.Element("fontSize").Value, out size);
            return size;
        }
        set {
            if (this.Settings == null) {
                this.Settings = XDocument.Load(@"c:\settings.xml");
            }
            this.Settings.Root.Element("fontSize").Value = value;
            this.Settings.Save(@"c:\settings.xml");
        }
    }

    //the users font color
    public string FontFamily {
        get {
            if (this.Settings == null) {
                this.Setting = XDocument.Load("c:\\settings.xml");
            }
            return this.Settings.Root.Element("fontFamily").Value;
        }
        set {
            if (this.Settings == null) {
                this.Settings = XDocument.Load("c:\\settings.xml");
            }
            this.Settings.Root.Element("fontFamily").Value = value;
            this.Settings.Save("c:\\settings.xml");
        }
    }

}

A bit extreme? I doubt it – In fact, I’m willing to bet some devs have found code like this floating around in a project. So after discussing and refactoring we normally end up with something like this…

//a user settings file
public class UserSettings {

    private const string SETTINGS_PATH = @"c:\settings.xml";
    private const string SETTING_FONT_COLOR = "fontColor";
    private const string SETTING_FONT_SIZE = "fontSize";
    private const string SETTING_FONT_FAMILY = "fontFamily";

    //holds the settings for the user
    public XDocument Settings {
        get {
            if (this._Settings == null) {
                this._Setting = XDocument.Load(SETTINGS_PATH);
            }
            return this._Settings;
        }
    }
    private XDocument _Settings;

    //saves the settings file
    public void _SaveSettings() {
        this.Settings.Save(SETTINGS_PATH);
    }

    //gets the value for a setting
    private string _GetSettingValue(string name) {
        return this.Settings.Root.Element(name).Value;
    }

    //modifies a setting value
    private string _ChangeSetting(string name, object value) {
        this.Settings.Root.Element(name).Value = value;
        this._SaveSettings();
    }

    //the users font color
    public string FontColor {
        get { return this._GetSettingValue(SETTING_FONT_COLOR); }
        set { this._ChangeSetting(SETTING_FONT_COLOR, value);  }
    }

    //the size of the font
    public int FontSize {
        get {
            int size = 0;
            int.TryParse(this._GetSettingValue(SETTING_FONT_SIZE), out size);
            return size;
        }
        set { this._ChangeSetting(SETTING_FONT_SIZE, value); }
    }

    //the users font type
    public string FontFamily {
        get { return this._GetSettingValue(SETTING_FONT_FAMILY); }
        set { this._ChangeSetting(SETTING_FONT_FAMILY, value); }
    }

}

… And then the blank looks appear — two lines? We saved a measly two lines? However, this is the part where you ask them to make a change both of the code samples. Here are a couple samples I like to use…

  1. Add 5 more settings as properties
  2. Change the path of the settings file to ‘D:\settings\storage.xml’
  3. Change the location of the setting from the root of the document to ‘user/settings/personal’
  4. Adjust the code so that you don’t get an error if the element name isn’t found

Of course, the point isn’t that using copy and paste is bad – but duplicating a bunch of lines instead of focusing on the architecture of your code can quickly lead to unmaintainable code. It is always worth your time to review your code and look for the areas that are repeating themselves..

Anyways, just something I was thinking about today – Go out there and code!

Anyways, just something I was thinking about today – Go out there and code!

… just kidding…

Written by hugoware

March 12, 2010 at 12:21 am

CSMongo – An Introduction

with 3 comments

Last night I did a short screencast explaining the basics of using Documents in CSMongo. The screencast goes over adding, changing and removing values along with getting access to the values you assign.

Written by hugoware

March 5, 2010 at 9:29 am

Woes With External Assemblies

leave a comment »

In all my years developing with .NET I haven’t ever had a situation that I needed to load an external library into my code at runtime. Normally, I simply add the reference to my project and I’m on my way. As it turns out it can be slightly tricky if you’re new to it.

I’m working on a project right now that is broken up into several pieces, one of which is a shared library that other external libraries use, specifically to inherit from a base class used by yet another part of the application. The shared class is actually referenced by each of the projects but one part of the application loads the assemblies in at runtime.

To summarize…

  1. SharedLibrary is referenced by MainApplication
  2. SharedLibrary is referenced by ExternalLibrary
  3. ExternalLibrary is loaded dynamically by MainApplication
  4. ExternalLibrary has classes that inherit from classes in SharedLibrary
  5. MainApplication needs to cast classes in ExternalLibrary into base class types found in SharedLibrary

So to illustrate what happens imagine two classes the abstract class ParentClass and the inheriting class ChildClass. This code made sense to me when I first typed it in but the results surprised me.

//create the instance
string path = @"d:\projects\libraries\exampleLibrary.dll";
object child = AppDomain.CurrentDomain
    .CreateInstanceFromAndUnwrap(path, "ExampleLibrary.ChildClass");
Type type = child.GetType();

//the details about the class
Console.WriteLine("Class is: {0}", child);
Console.WriteLine("Type Name: {0}", type.FullName);
Console.WriteLine("Load : {0}", type.Assembly.FullName);
Console.WriteLine("Ref  : {0}", typeof(ParentClass).Assembly.FullName);
Console.WriteLine("Is ChildClass  : {0}", child is ChildClass);
Console.WriteLine("Is ParentClass : {0}", child is ChildClass);
Console.WriteLine("Casting : {0}", child as ParentClass);

//the results
//Class is: ExampleLibrary.ChildClass
//Type Name: ExampleLibrary.ChildClass
//Load : ExampleLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
//Ref  : ExampleLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
//Is ChildClass  : False
//Is ParentClass : False
//Casting : <-- which is null

What the heck? Admittedly, I don’t know why this fails – If I had to guess it would have something to do… well… we will discuss it in a moment…

I ended up with the following solution which worked exactly the way I had hoped.

//load the assembly manually
string path = @"d:\projects\libraries\exampleLibrary.dll";
byte[] bytes = File.ReadAllBytes(path);
AppDomain.CurrentDomain.Load(bytes);

//create the instance
object child = AppDomain.CurrentDomain
    .CreateInstanceAndUnwrap("ExampleLibrary", "ExampleLibrary.ChildClass");
Type type = child.GetType();

//the details about the class
Console.WriteLine("Class is: {0}", child);
Console.WriteLine("Type Name: {0}", type.FullName);
Console.WriteLine("Load : {0}", type.Assembly.FullName);
Console.WriteLine("Ref  : {0}", typeof(ParentClass).Assembly.FullName);
Console.WriteLine("Is ChildClass  : {0}", child is ChildClass);
Console.WriteLine("Is ParentClass : {0}", child is ChildClass);
Console.WriteLine("Casting : {0}", child as ParentClass);

//the results
//Class is: ExampleLibrary.ChildClass
//Type Name: ExampleLibrary.ChildClass
//Load : ExampleLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
//Ref  : ExampleLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
//Is ChildClass  : True
//Is ParentClass : True
//Casting : ExampleLibrary.ChildClass

Hmm… Not what I was expecting…

Even still, the code might be working – but one thing I always tell other developers is “that’s great your code is working – now, why is it working?”

After poking around for a bit I noticed something that really should have been obvious from the beginning. Take a look at this code and you’ll see where I’m headed…

Console.WriteLine("Load : {0}", type.Assembly.Location);
Console.WriteLine("Ref  : {0}", typeof(ParentClass).Assembly.Location);

//Load : d:\projects\libraries\exampleLibrary.dll
//Ref  : d:\projects\application\bin\debug\exampleLibrary.dll

Oh… so maybe not exactly the same type after all… 😐

I suspect that by loading the bytes in directly took the whole ‘location’ thing out of picture. Of course, I need to read up on this some more before I’m positive but if any of you genius developers out there can confirm my theory then it would be greatly appreciated.

Written by hugoware

March 4, 2010 at 12:29 am

CSMongo On GitHub

with 16 comments

I’ve been hammering away at the keyboard for a few weeks now and my beta version of CSMongo is now live on GitHub!

I’ve written a lot of documentation in for the project in the Wiki section on the site, so if you’re interested you can start reading the details of how the code works.

In this post I’ll just go over some of the more interesting things the CSMongo does to make working with Mongo easier than normal.

Anonymous Types and Dynamic Creation

Working with documents, including those with multiple levels, is a snap with CSMongo. You have many ways to make changes but each of these ways also allow you to create entirely new sections of your document.

MongoDocument document = new MongoDocument();

//add some new items
document += new {
    name = "Hugo",
    age = 30,
    settings = new {
        font = "arial",
        color = "orange"
    }
};

//set values for a lower level field
document.Set("browser.urls", new object [] {
    new { site = "google.com", favorite = false },
    new { site = "hugoware.net", favorite = true, icon = "blue-icon.ico" }
});

//or create new elements with an index path
document["blog.url"] = "https://somewebguy.wordpress.com";

//or use existing objects as a template
Uri address = new Uri("http://www.herdingcode.com");
document += address;

Nifty Anonymous Type Mapping Method TM

One thing that I didn’t like was always having to refer to elements using a string. I had a moment late one night … er… maybe it was early one moring… like around 1AM or so — Why can’t I use a return anonymous type value to act as a template for the values to use. Here is an example of what I put together…

//grab a document - Assume that the value returned
//looks like the JSON example below
MongoDocument document = database.Find("users").SelectOne();
//{
//    name : "Jimmy",
//    age : 50,
//    website : "http://www.hugoware.net"
//    settings : {
//        font : "arial",
//        color : "red"
//    }
//}

//provide an anonymous type as the template to return
//Notice the values provide match in some places but not 
//in other areas
var value = document.Get(new {
    name = "no name",
    age = -1,
    admin = false,
    favorite = Mongo.Null,
    settings = new {
        color = "black",
        version = 0
    }
});

//now we have an anonymous type with the values
//it could find and then the default values for the
//sections it couldn't find or was missing
value.name; // "Jimmy"
value.age; // 50
value.admin; // false (fallback)
value.favorite; // null (fallback)
value.settings.color; // "red"
value.settings.version; // 0

This means that we can refactor our code without needing to update a bunch of string values. You can even use this mapping option directly after a query so all of the values are converted into the new type automatically.

Lazy Collections

When you’re making changes to a MongoDatabase, for the most part all of the commands are immediate – meaning they connect to the database right away and perform their work. However, most commands inside of a MongoCollection don’t execute until you call the SubmitChanges method. This can allow you to queue up a few inserts and deletes or make changes to documents that have been loaded into memory and then update them all at once. The code below illustrates how it works.

//Inserting Documents
//=================================
MongoCollection collection = database.GetCollection("users");

//add a few documents
collection.InsertOnSubmit(new { name = "Hugo" });
collection.InsertOnSubmit(new { name = "Mary" });
collection.InsertOnSubmit(new { name = "Gwyn" });
collection.InsertOnSubmit(new { name = "Cassie" });

//nothing has been inserted yet, do it now
collection.SubmitChanges();

//Performing Updates
//=================================
MongoCollection collection = database.GetCollection("users");
var documents = collection.Find()
    .Greater("age", 50)
	.Select();
	
//make each of the changes
foreach(MongoDocument document in documents) {
    document += { isOld = true };
}

//documents are only changed in memory
//send the update to the server
collection.SubmitChanges();

//Deleting Documents
//=================================
MongoCollection collection = database.GetCollection("users");
var documents = collection.Find()
    .Greater("age", 50)
	.Select();
	
//mark each for deletion
foreach(MongoDocument document in documents) {
    collection.DeleteOnSubmit(document);
}

//documents are only changed in memory
//send the update to the server
collection.SubmitChanges();

Of course, you can perform a blanket update statement using a query from the MongoDatabase object itself, but this is a good example of how a MongoCollection can do it in a lazy way.

It is also worth mentioning that if you use the GetCollection method to work with a collection then it is also monitored by the database. This means that if you call the MongoDatabase method SubmitChanges then it will check each of the collections it is tracking and submit their changes automatically.

Try It Out!

If you’ve haven’t been sure if you want to try out MongoDB yet then now is a great time! The links below can help you get started!

Getting Started
MongoDB Website
Using MongoDB With Visual Studio

Source Code
CSMongo on GitHub
CSMongo Wiki Help on GitHub

Written by hugoware

March 1, 2010 at 12:35 am