Archive for March 2010
Static Constructors — What? What???
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!)
A Personable Form Of Impersonation
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.
Performing Updates With CSMongo
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.
MVC2 As A “WebOS”
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?
Copyin’ and-a Pastin’
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…
- Add 5 more settings as properties
- Change the path of the settings file to ‘D:\settings\storage.xml’
- Change the location of the setting from the root of the document to ‘user/settings/personal’
- 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…
Woes With External Assemblies
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…
- SharedLibrary is referenced by MainApplication
- SharedLibrary is referenced by ExternalLibrary
- ExternalLibrary is loaded dynamically by MainApplication
- ExternalLibrary has classes that inherit from classes in SharedLibrary
- 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.
CSMongo On GitHub
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