Steve Cook's WebLog

Using LINQ to write constraints in OCL style

I wanted to investigate using LINQ to write constraints "OCL-style".   I made a "minimal language" and added a new NamedDomainClass called Property, embedded in ExampleElement.  Then I wrote the following validation method to check that all of the Properties attached to an ExampleElement are uniquely named:

[ValidationState(ValidationState.Enabled)]

public partial class ExampleElement

{

    [ValidationMethod(ValidationCategories.Menu | ValidationCategories.Save)]

    private void TestExampleElement(ValidationContext context)

    {

        var propnames = from p in this.Properties select p.Name;

        var distinctnames = propnames.Distinct<string>();

        if (propnames.Count<string>() != distinctnames.Count<string>())

        {

            context.LogError("Non-unique property names", "Error 1");

        } 

    }

} 

 

A lot tighter than writing it in good old C#, I think.  Then I thought about "flattened sets", i.e. navigating across more than one relationship and creating a single collection containing the results.  So I created SubProperty embedded in Property, and extended the constraint like this:

 

[ValidationState(ValidationState.Enabled)]

public partial class ExampleElement

{

    [ValidationMethod(ValidationCategories.Menu | ValidationCategories.Save)]

    private void TestExampleElement(ValidationContext context)

    {

        var propnames = from p in this.Properties select p.Name;

        var distinctnames = propnames.Distinct<string>();

        if (propnames.Count<string>() != distinctnames.Count<string>())

        {

            context.LogError("Non-unique property names", "Error 1");

        }

 

        var subproperties = this.Properties.Aggregate(Enumerable.Empty<SubProperty>(),

                                                        (agg, p) => agg.Union<SubProperty>(p.SubProperties));

        var subpropnames = from p in subproperties select p.Name;

        var distinctsubpropnames = subpropnames.Distinct<string>();

 

        if (subpropnames.Count<string>() != distinctsubpropnames.Count<string>())

        {

            context.LogError("Non-unique sub property names", "Error 2");

        }

    }

}

 

Not bad: but that line to calculate and flatten the subproperties is a bit complicated.  So, inspired by OCL, I defined a new extension method like this:

 

public static class C

{

    public static IEnumerable<U> Collect<T, U>(this IEnumerable<T> source, Func<T, IEnumerable<U>> func)

    {

        return source.Aggregate(Enumerable.Empty<U>(), (agg, p) => agg.Union<U>(func(p)));

    }

}

 

And now the subproperties line looks like this:

 

var subproperties = this.Properties.Collect(p => p.SubProperties);

 

I think I may be using that Collect method again!

 

Published Thursday, August 23, 2007 3:18 PM by Steve Cook
Filed under:

Comments

 

MSDN Blog Postings » Using LINQ to write constraints in OCL style said:

August 23, 2007 11:26 AM
 

Michael Giagnocavo said:

Two questions:

- Doesn't LINQ's SelectMany do what you want to do (flatten collections)?

- Out of curiosity, why do you explicitly specify the type parameter to methods like Count (Count<string>() instead of just count())?

Thanks!

August 23, 2007 11:48 AM
 

Steve Cook said:

SelectMany - I will have to try that.

The type parameters to Count and Distinct are unnecessary, as you point out.

-- Steve

August 23, 2007 11:58 AM
 

Michael Giagnocavo said:

Oh one other style question (I'm very curious about how people view functional programming):

You wrote:

var propnames = from p in this.Properties select p.Name;

Instead of:

var propnames = this.Properties.Select(p => p.Name);

Any particular reason?

August 23, 2007 12:07 PM
 

Steve Cook said:

Really no reason at all.  I haven't decided which I prefer yet.  I suppose to be consistent with OCL I would go for the second style.

You are correct about SelectMany - it does just the same as my Collect.  So Collect is unnecessary.  I guess the name confused me. Still, writing the extension method was a good learning experience.

August 23, 2007 12:29 PM
 

Michael Giagnocavo said:

Cool! I love seeing functional styles; _please_ keep blogging things like this. Hopefully, more people will see why this is vastly superior to writing it imperatively. In fact, do you have a snippet of what it would look like without the C# 3.0 features, for comparison?

August 23, 2007 2:03 PM
 

Steve Cook said:

I guess doing it the “old way” would typically look like this:

[ValidationMethod(ValidationCategories.Menu | ValidationCategories.Save)]

private void TestExampleElementTheOldWay(ValidationContext context)

{

   List<string> uniquePropertyNames = new List<string>();

   foreach (Property p in this.Properties)

   {

       if (uniquePropertyNames.Contains(p.Name))

       {

           context.LogError("Non-unique property names (old way)", "Error 3");

       }

       else

       {

           uniquePropertyNames.Add(p.Name);

       }

   }

   List<string> uniqueSubPropertyNames = new List<string>();

   foreach (Property p in this.Properties)

   {

       foreach (SubProperty sp in p.SubProperties)

       {

           if (uniqueSubPropertyNames.Contains(sp.Name))

           {

               context.LogError("Non-unique sub property names (old way)", "Error 4");

           }

           else

           {

               uniqueSubPropertyNames.Add(sp.Name);

           }

       }

   }

}

August 24, 2007 5:37 AM
New Comments to this post are disabled

This Blog

Syndication

News

Locations of visitors to this page
Disclaimer

The information in this weblog is provided "AS IS" with no warranties, and confers no rights. This weblog does not represent the thoughts, intentions, plans or strategies of my employer. It is solely my opinion. Inappropriate comments will be deleted at the author’s discretion. All code samples are provided "AS IS" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.


© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker