Hooked on LINQ

Hooked on LINQ - Developers' Wiki
for .NET Language Integrated Query

Quick Search

Advanced Search »
Update is an extension method that allows you to update properties on objects that are the result of a query. Whilst the standard query operators support retrieving data objects from a collection, the standard update SQL type clause is missing. A consistent pattern of querying data, then foreach'ing over that data is replicated many, many times when using LINQ to Objects. The Update extension method aims to remove the need for as many foreach loops by allowing you to achieve property assignments on the resulting objects of a query within the one query statement block.

My original vision was to create an extension method that allows this type of query syntax:

int count = (from d in drawingObjects
             where d.IsSelected && d.Color == Colors.Blue
             select d)
             .Update(do => { do.Color = Color.Red; do.Selected = false; } );

or using Extension Method syntax:

int count = drawingObjects
            .Where(d => d.IsSelected && d.Color == Colors.Blue)
            .Update(do => { do.Color = Color.Red; do.Selected = false; } );

The Update extension method simply iterates an IEnumerable<T> source and calls a void return type delegate statement block. The Lambda Expression syntax allows you to pass in multiple statements enclosed in curly-braces (you can drop them if there is only one assignment being made), although the May 2006 LINQ CTP editor support will show syntax errors, you can still compile. The January Orcas CTP does not suffer this editor syntax issue, and it works perfectly.

Email me comments, suggestions and feedback. See my contact details here.

January CTP Code and Unit Tests

// Copyright (C) 2007 Troy Magennis. All Rights Reserved.
// You are free to use this material, however you do so AT YOUR OWN risk. 
// You are prohibited from removing this disclaimer or copyright notice from any derivitive works.
// Remember to visit http://www.hookedonlinq.com - The LINQ wiki community project.
 
using System;
using System.Collections.Generic;
 
namespace Aspiring.Linq {
 
    public static class UpdateExtensions {
 
        public delegate void Func<TArg0>(TArg0 element);
 
        /// <summary>
        /// Executes an Update statement block on all elements in an IEnumerable<T> sequence.
        /// </summary>
        /// <typeparam name="TSource">The source element type.</typeparam>
        /// <param name="source">The source sequence.</param>
        /// <param name="update">The update statement to execute for each element.</param>
        /// <returns>The numer of records affected.</returns>
        public static int Update<TSource>(this IEnumerable<TSource> source, Func<TSource> update)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (update == null) throw new ArgumentNullException("update");
            if (typeof(TSource).IsValueType)
                throw new NotSupportedException("value type elements are not supported by update.");
 
            int count = 0;
            foreach (TSource element in source)
            {
                update(element);
                count++;
            }
            return count;
        }
    }
}



// Copyright (C) 2007 Troy Magennis. All Rights Reserved.
// You are free to use this material, however you do so AT YOUR OWN risk. 
// You are prohibited from removing this disclaimer or copyright notice from any derivitive works.
// Remember to visit http://www.hookedonlinq.com - The LINQ wiki community project.
 
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using NUnit.Framework;
using Aspiring.Linq;
 
namespace Aspiring.Linq.UnitTests
{
    [TestFixture]
    public class UpdateTests {
 
        public class TestClass {
            public int I;
            public string S;
            public bool B = false;
        }
 
        [Test]
        [ExpectedException("System.ArgumentNullException")]
        public void UpdateNullSourceTest() {
            List<TestClass> values = null;
            values.Update(u => u.I = 1);
        }
 
        [Test]
        [ExpectedException("System.ArgumentNullException")]
        public void UpdateNullUpdateTest() {
            List<TestClass> values = new List<TestClass>();
            values.Update(null);
        }
 
        [Test]
        [ExpectedException("System.NotSupportedException")]
        public void UpdateValueTypeElementTest() {
            List<int> values = new List<int>();
            values.Update(u => u = 1);
        }
 
        [Test]
        public void UpdateClassBasicTest() {
            List<TestClass> values = new List<TestClass> { 
                new TestClass { I = 1, S = "one" },
                new TestClass { I = 2, S = "two" },
                new TestClass { I = 3, S = "three" },
                new TestClass { I = 4, S = "four" },
                new TestClass { I = 5, S = "five" }
            };
                                                            
            // no matching elements to change
            int count0 = (from t in values
                          where t.I > 100
                          select t).Update(tc => { tc.S = tc.I.ToString(); tc.B = true; } );
            
            Assert.AreEqual(0, count0, "Incorrect count returned"); 
           
            // 3 matching elements to change
            int count = (from t in values
                         where t.I < 4
                         select t).Update(tc => { tc.S = tc.I.ToString(); tc.B = true; } );
 
            // or, using extension method syntax
            //int count = values.Where(t => t.I < 4)
            //                  .Update(tc => { tc.S = tc.I.ToString(); tc.B = true; } );
            
            Assert.AreEqual(3, count, "Incorrect count returned"); 
            
            Assert.AreEqual("1", values[0].S);
            Assert.AreEqual("2", values[1].S);
            Assert.AreEqual("3", values[2].S);
            Assert.AreEqual("four", values[3].S);
            Assert.AreEqual("five", values[4].S);
 
            Assert.AreEqual(true, values[0].B);
            Assert.AreEqual(true, values[1].B);
            Assert.AreEqual(true, values[2].B);
            Assert.AreEqual(false, values[3].B);
            Assert.AreEqual(false, values[4].B);
        }
    }
}



May 2006 CTP Code and Unit Tests

// Copyright (C) 2007 Troy Magennis. All Rights Reserved.
// You are free to use this material, however you do so AT YOUR OWN risk. 
// You are prohibited from removing this disclaimer or copyright notice from any derivitive works.
// Remember to visit http://www.hookedonlinq.com - The LINQ wiki community project.
 
using System;
using System.Collections.Generic;
 
namespace Aspiring.Query {
 
    public static class UpdateExtensions {
 
        public delegate void Func<TArg0>(TArg0 element);
    
        /// <summary>
        /// Executes an Update statement block on all elements in an IEnumerable<T> sequence.
        /// </summary>
        /// <typeparam name="TSource">The source element type.</typeparam>
        /// <param name="source">The source sequence.</param>
        /// <param name="update">The update statement to execute for each element.</param>
        /// <returns>The numer of records affected.</returns>
        public static int Update<TSource>(this IEnumerable<TSource> source, Func<TSource> update) {
            if (source == null) throw new ArgumentNullException("source");
            if (update == null) throw new ArgumentNullException("update");
            if (typeof(TSource).IsValueType) 
                throw new NotSupportedException("value type elements are not supported by update.");
 
            int count = 0;
            foreach(TSource element in source) {
                update(element);
                count++;
            }
            return count;
        }
    }
}



using System;
using System.Collections.Generic;
using System.Text;
using System.Query;
using NUnit.Framework;
using Aspiring.Query;
 
namespace Aspiring.Query.UnitTests
{
    [TestFixture]
    public class UpdateTests {
 
        public class TestClass {
            public int I;
            public string S;
            public bool B = false;
        }
 
        [Test]
        [ExpectedException("System.ArgumentNullException")]
        public void UpdateNullSourceTest() {
            List<TestClass> values = null;
            values.Update(u => u.I = 1);
        }
 
        [Test]
        [ExpectedException("System.ArgumentNullException")]
        public void UpdateNullUpdateTest() {
            List<TestClass> values = new List<TestClass>();
            values.Update(null);
        }
 
        [Test]
        [ExpectedException("System.NotSupportedException")]
        public void UpdateValueTypeElementTest() {
            List<int> values = new List<int>();
            values.Update(u => u = 1);
        }
 
        [Test]
        public void UpdateClassBasicTest() {
            List<TestClass> values = new List<TestClass> { 
                new TestClass { I = 1, S = "one" },
                new TestClass { I = 2, S = "two" },
                new TestClass { I = 3, S = "three" },
                new TestClass { I = 4, S = "four" },
                new TestClass { I = 5, S = "five" }
            };
                                                            
            // no matching elements to change
            int count0 = (from t in values
                          where t.I > 100
                          select t).Update(tc => { tc.S = tc.I.ToString(); tc.B = true; } );
            
            Assert.AreEqual(0, count0, "Incorrect count returned"); 
           
            // 3 matching elements to change
            int count = (from t in values
                         where t.I < 4
                         select t).Update(tc => { tc.S = tc.I.ToString(); tc.B = true; } );
 
            // or, using extension method syntax
            //int count = values.Where(t => t.I < 4)
            //                  .Update(tc => { tc.S = tc.I.ToString(); tc.B = true; } );
            
            Assert.AreEqual(3, count, "Incorrect count returned"); 
            
            Assert.AreEqual("1", values[0].S);
            Assert.AreEqual("2", values[1].S);
            Assert.AreEqual("3", values[2].S);
            Assert.AreEqual("four", values[3].S);
            Assert.AreEqual("five", values[4].S);
 
            Assert.AreEqual(true, values[0].B);
            Assert.AreEqual(true, values[1].B);
            Assert.AreEqual(true, values[2].B);
            Assert.AreEqual(false, values[3].B);
            Assert.AreEqual(false, values[4].B);a
        }
    }
}



If you would like to comment on this page, click on the Discuss button located on the top-right of each page. Feel free to edit any mistakes or omissions you find. If you have an objection or find in-appropriate content then contact the administrator. This website is not affiliated with Microsoft®, all content and opinions are those of the specific author and some advice, solutions and article may contain unintentional errors - please use care. Powered by ScrewTurn Wiki version 2.0.33. Some of the icons created by FamFamFam.