Archive

Archive for the ‘LINQ’ Category

Crazy Way To Set Display Order From an Enum

I’m sure someone has a better way to do this and I’m all ears; however, this seems to ‘work’ and performs well enough (YAGNI fully applied).

We have an Enum of our different “school levels”–elementary, middle, high schools, special schools, etc.  Since the Enum’s values correspond to the data values in our student system, their display/alphabetical orders do not match the common order (e.g. High School = 4, Middle School = 5).

I came up with a simple attribute that’s added to the Enum to “specify” display order and it’s worked like a champ for the past year or so.

[Description("Elementary")]
[DisplayOrder(1)]
Elementary = 2,

[Description("Middle")]
[DisplayOrder(2)]
Middle = 5,

Not at all fancy.

However useful it is at looping and providing context in code, what about using it for ordering OTHER “level” information from another source?  So far, I haven’t found a clean way.

The “Solution”?

Use the index of our already-ordered Enum list.  In this instance, our model’s SchoolLevel property matches the Description of the Enum.

            var levelsInOrder = Enum<level>.ToList();
            // AutoMapper mappings, etc.

            model.Data = data
                .OrderBy(x => levelsInOrder
                    .FindIndex(z => z.Description() == x.SchoolLevel));

Is it perfect? No, but it works. I could probably even refactor the Enum list and FindIndex call out for a bit more clarity.

        model.Data = data.OrderBy(x => x.GetDisplayOrder(x.SchoolLevel));

        ...

        private int GetDisplayOrder(string level)
        {
            return Enum<level>.ToList().FindIndex(x => x.Description() == level);
        }

Is there a better way?

Categories: .net 3.0, c#, LINQ Tags: , ,

Filtering an Enum by Attribute

July 9, 2009 Comments off

I had a curve ball thrown at me this morning—changing requirements.  It happens and was easily solved by a couple of custom attributes and a helper method.

UPDATE: I’ve updated the code (and explaination) for FilterEnumWithAttributeOf below to tidy it up a bit.

In our current project, there is an enum of standard, static “periods” (times of days students are in school).  Easy enough.

BeforeSchool = 0,
FirstPeriod = 1,
SecondPeriod = 2,
etc.

But what happens if we want to “query” our list down a bit… say a certain group only wanted a subset of the “periods”.

I could create an entirely different Enum — Group1Period and Group2Period.

But then handling things in FluentNHibernate’s automapping would get freaked out with the Period property.

So, what about a custom attribute?

  1. I can assign multiple custom attributes to the same Enum field so I can be in Group1 and Group2 at the same time.
  2. I can keep the same Enum “Period” for my ORM layer.
  3. Now how do I query it down…?

Here’s an abstracted example of how the enum looks right now:

public enum Period

{

    [Elementary][Secondary]

    [Description(“Before School”)]

    BeforeSchool = 0,

 

    [Elementary]

    Homeroom = 12,

 

    [Secondary]

    [Description(“1st”)]

    First = 1,

}

Elementary and Secondary (our two groups, in this case) are “logicless” attributes (I’m just looking at them as flags, not passing/storing information).

[AttributeUsage(AttributeTargets.Field)]

public class ElementaryAttribute : Attribute

{

}

 

[AttributeUsage(AttributeTargets.Field)]

public class SecondaryAttribute : Attribute

{

}

Now, to filter out those pesky periods based on the attributes.

Update:

Old Code!

public IEnumerable<TEnum> FilterEnumWithAttributeOf<TEnum, TAttribute>()

{

    foreach (var field in

        typeof (TEnum).GetFields(BindingFlags.GetField |

                                 BindingFlags.Public |

                                 BindingFlags.Static))

    {

        foreach (var attribute in

            field.GetCustomAttributes(typeof (TAttribute), false))

        {

            yield return (TEnum) field.GetValue(null);

        }

    }

}

New Code!

public static IEnumerable<TEnum> FilterEnumWithAttributeOf<TEnum, TAttribute>()

    where TEnum : struct

    where TAttribute : class

{

    foreach (var field in

        typeof(TEnum).GetFields(BindingFlags.GetField |

                                 BindingFlags.Public |

                                 BindingFlags.Static))

    {

 

        if (field.GetCustomAttributes(typeof(TAttribute), false).Length > 0)

            yield return (TEnum)field.GetValue(null);

    }

}

Why new code?

Well, after looking over the code, I don’t need to iterate through each attribute, simply see if the field contains it (Length > 0).  If it does, then return it.  That cuts a loop out of our code and performs the same function.  I also added two generic constraints.  You can’t constrain by Enum, but struct works well.

I’m passing in two generics in this case—TEnum, which is the type of the of the Enum and TAttribute.. the type of the attribute.  Yeah, I realize that my creativity of naming is pretty low.  Work with me here, alright? 😉

Past that, the loops are pretty easy.

  1. Loop through each field of the enumeration.  Return the field (GetField) and be sure to check Public and Static fields.
  2. Loop through each custom attribute on each field (returned by GetField) and only return the fields that match the type of our attribute.  I pass along the false parameter (do not inherit) because I’m not interested in inherited attributes. You could leave this as true. YMMV.
  3. If the field’s attribute’s contains our type, yield out the actual Enum value (a string of the field isn’t as useful).

Now, for using it…

var enums = FilterEnumWithAttributeOf<Period, ElementaryAttribute>();

 

foreach (var period in enums)

{

    Console.WriteLine(“{0}, {1}”.AsFormatFor(period, (int)period));

}

Easy enough.  ElementaryAttribute returns:

BeforeSchool, 0
Homeroom, 12
AfterSchool, 10
etc..

Running the same code, but asking for SecondaryAttribute returns:

BeforeSchool, 0
First, 1
Second, 2
etc..

Sweet.

Tags: , ,

Benchmarks : Comparing LINQ to NHibernate Transforms/Grouping

Yesterday, I wrote about setting up NHibernate to query up, group, count, and transform results and return them into a control.  Why did I go to such effort?  Well, the original LINQ query I had that refined the results didn’t perform up to par.  As some may note, premature optimization is never a good practice so I needed some stats to back up the claims.

Overnight, I wrote up a quick test to query up both ways and benchmark the results.  Here’s what I found.

The “test”:

public void TEMP_quick_compare_of_linq_to_nhibernate()

{

    var schoolId = 120;

 

    var benchmark = new Benchmark();

    using (var repository = new IncidentRepository())

    {

        benchmark.Start();

        var resultsFromLinq =

            repository.GetCountByIncidentCodeWithLinq(schoolId);

        foreach (var item in resultsFromLinq)

        {

            Console.WriteLine(item);

        }

        benchmark.Stop();

        Console.WriteLine(“Linq: {0}”.AsFormatFor(benchmark.ElapsedTime));

 

        benchmark.Start();

        var resultsFromNhibernate =

            repository.GetCountByIncidentCode(schoolId);

        foreach (var item in resultsFromNhibernate)

        {

            Console.WriteLine(item);

        }

        benchmark.Stop();

        Console.WriteLine(“NHibernate: {0}”.AsFormatFor(benchmark.ElapsedTime));

    }

}

Setting up the benchmark (and the NHibernate Init) are outside of the benchmark—they’re necessary overhead.  I’m also iterating through each of the results as part of the benchmark to ensure that everything is evaluated. Past that, pretty basic.  On the database side, I’ve disabled statement caching to not sway the results as much.

With 24 records (the test data in the system), the results were pretty clear. The average of running the benchmark 100 times resulted in…

Linq: 00:00:00.7050000
NHibernate: 00:00:00.0190000

With 24 records, NHibernate was about 37x faster. 

That’s nice, but what happens in a few weeks when there are a few thousand records?  I populated a few hundred of each incident type into the system, giving me almost 4000 records (the anticipated monthly load of the system by the customer).  How’d that change our averages?

Linq: 00:00:00.8869746
NHibernate: 00:00:00.1381518

Now we’re only 6x faster with NHibernate vs. LINQ.  The duration from 24 to 4000 records for LINQ  jumped ~.18 seconds for a 25% gain where as NHibernate jumped ~.11 seconds for a 626% gain.

So, with that, my original gut feeling and assumptions were wrong.  More and more records don’t really slow down the LINQ filtering.. at least not by much.  The performance gain is still appparent between the two methods (.88 sec vs. .13 sec); however, how much of that time is eaten up by rendering, server latency, etc and not by the actual processing?

A Flexible “Is In Range” Extension Method

I’m working out some business rules for an application that allows the end user to specify whether or not to direct records by the first letter of the last name of a student or by the grade of the student.

The quick extension method looks like this:

public static bool IsInRange<T>(this T value, T start, T end)

where T: IComparable<T>

{

return value.CompareTo(start) >= 0 && value.CompareTo(end) <= 0;

}

Our tests:

[Fact]

public void IsInRange_returns_correct_boolean_for_comparison()

{

9.IsInRange(1, 10).ShouldBeTrue();

“L”.IsInRange(“A”, “J”).ShouldBeFalse();

“A”.IsInRange(“A”, “A”).ShouldBeTrue();

“B”.IsInRange(“A”, “A”).ShouldBeFalse();

12302.IsInRange(1, 10).ShouldBeFalse();

 

“Bob”.IsInRange(“A”, “A”).ShouldBeFalse();

“Smith”.IsInRange(“A”, “Z”).ShouldBeTrue();

}

Everything works well… I haven’t tested all of the permutations and types yet… but it gets me out of the jam I’m in right now.

Is there a better way? 😀

Fluent NHibernate With Oracle – A Question…

December 2, 2008 3 comments

Answer found!  Read more here.

After success with standard NHibernate, I quickly became enraptured with the cleaner Fluent NHibernate API.  No more XML; however, I have had VERY limited (read: none) success getting this working against Oracle—especially against our crazy Oracle environment (of which I have no control and readonly access).

Here’s what I have:

A basic ClassMap of a Student object.

class StudentMap : ClassMap<Student>

{

       public StudentMap()

       {

              WithTable(“Students”);

 

              Id(x => x.Id, “pupil_number”)

                     .GeneratedBy.Native();

              Map(x => x.FirstName, “first_name”);

              Map(x => x.LastName, “surname”);

 

       }

}

A basic Student object.

public class Student

{

       private Student() { }

 

       public virtual int Id { get; set; }

       public virtual string FirstName { get; set; }

       public virtual string LastName { get; set; }

}

Note: My constructor is private because this SHOULD be a readonly object.  Setting it public doesn’t seem to matter either. 😦

A basic data provider.

public class SISDataProvider

{

       public ISession Session;

 

       public SISDataProvider(ISession session)

       {

              Session = session;

       }

 

       public T GetById<T>(int id)

       {

              Session.Flush();

              return Session.Get<T>(id);

       }

}

No save functionality—again, we just have readonly access to this database.

A basic session manager.

public class SISSessionManager

{

       private readonly string _mappingAssembly;

       private readonly ISessionFactory _sessionFactory;

 

       public SISSessionManager()

       {

              _mappingAssembly = “SIS.Domain”;

              _sessionFactory = GetSessionFactory();

       }

 

       public ISession GetSession()

       {

              return _sessionFactory.OpenSession();

       }

 

       private ISessionFactory GetSessionFactory()

       {

              var config = new Configuration();

 

              var configuration = OracleConfiguration.Oracle9

                .ConnectionString

                    .Server(“server”)

                    .Port(1521)

                    .Instance(“instance.name”)

                    .Username(“user”)

                    .Password(“password”)

                    .Create

                .ConfigureProperties(config);

 

              configuration.AddMappingsFromAssembly(Assembly.Load(_mappingAssembly));

 

              return configuration.BuildSessionFactory();

       }

}

The OracleConfiguration is based on a FluentNhibernate.Cfg.PersistanceConfiguration that I created that calls the OracleClientDriver (not the ODP; that’s OracleDataClientDriver—I’m simply wanting to get the architecture working for now and I’ll dink with increasing performance with ODP as the next step).

Finally, the test.

public class creating_a_student

{

       private SISDataProvider _provider;

       private SISSessionManager _sessionFactory;

 

       public creating_a_student()

       {

              _sessionFactory = new SISSessionManager();

              _provider = new SISDataProvider(_sessionFactory.GetSession());

       }

 

       [Fact]

       public void with_a_valid_student_id_selects_the_correct_student()

       {

             var sut = _provider.GetById<Student>(12345);

             sut.Should_Not_Be_Null();

             sut.Id.Should_Be_Equal_To(12345);

       }

}

Unfortunately, all that and I get an error:

creating_a_student.with_a_valid_student_id_selects_the_correct_student : Failed

XunitException: NHibernate.MappingException : No persister for: SIS.Domain.Student

After some debugging, the Sis.Domain.Student is being added to the Mapping collection, but I’m not sure if that’s the problem or not.  A Google of “no persister” pulls up a few NHibernate hits for not having the XML files set to Embedded Resource, which isn’t my problem here—is it?  I thought Fluent NHibernate escaped XMLHell.

I’d appreciate any guidance on this—I’m stumped.

VS2008 and .NET 3.5 SP1 Success

August 12, 2008 Comments off

Danger, danger!Well, I’m 1/4 now upgrading the VMs to SP1.  The “one” that worked is the one I least expected.

Machine 1:

A simple VM used for a basic web project.  C#, Web Developer Tools installed—not even joined to the network.  No “hotfixes” were installed, but I ran the tool anyway just to be sure.  It chugged for a while and then threw an exception.  Feeling daring, I went ahead and tried to upgrade to SP1 and it didn’t even detect Visual Studio or .NET 3.5.  Hah.

Machine 2:

A prototyping VM that I copy web apps onto to run and test—nothing major, just VS2008, C#, web test tools, etc.  This one did have SP1 beta on it that I was dinking with as well as various Silverlight tools.  The patch removal tool worked just fine.  After a reboot, the SP1 installation got about 20% complete then McAfee (mcshield.exe) threw about 70 exceptions all at once and flooded the task bar.  The system then BSOD’d and reboot.  I tried another couple times to install SP1 (I can’t disable McAfee, ePO prevents it) with no success.

Machine 3:

This VM provides a test system for our customers to connect to, evaluate applications, and such.  VS2008 and .NET 3.5 (no SP1 beta) was installed more for convenience than anything else.  The system isn’t in use for the next few weeks, so worth a shot on this one.  I didn’t run the patch removal tool (perhaps that was my mistake) on this workstation and proceeded with installing SP1.  About 80% through the installation, devenv.exe crashed and asked to be debugged—of course, when I tried to debug, it crashed.  Hah.

Machine 4:

This is the one that worked; however, it’s the oddest, most broken of the bunch.  It’s had SP1 beta on it, various MVC preview builds, Astoria, bunches of Silverlight tools as well as various libraries and code bunches I have for newsgroup and forum posts.  The patch removal tool found a bunch of stuff and remove it without any issues.  After the reboot, SP1 installed (took about 45 minutes) and the system reboot.

And it works…

It works!

Seriously… why?

I still have a few VMs left to dink with and plan to throw it on my development workstation later this week after I wrap up a few projects and have some down time (I had planned to rebuild the entire workstation anyway—so good opportunity).

I hope, after I find some basic installation method, to have time to dig in to the features…

IQueryable Methods on ActiveReports ControlCollections

I’m currently working on a project with an extremely complex, multi-page report.  Unfortunately, a customer requirement was EXACT typography to the “Excel” report and there are hundreds (literally) of data points randomly on the page. 

There isn’t a good way to iterate through the results (it’s a very detailed grade card for elementary students) and still match the layout requirements.

So, I went about “drawing” it out—lots of Label controls, lines, boxes, and such.  To save myself a bit of time assigning DataFields, querying results, etc., I opted for a different kind of iteration—control iteration.

My schema was simple: q#i#, for the quarter and the primary data point of the report, the indicator Id.  Since my business object, a Report, was already assigned to the ActiveReport document, I could simply iterate away!

Unfortunately, if you try to determine if the detail.Controls (a ControlCollection) contains a control and it doesn’t exist—it doesn’t simply return null, it throws an exception.  In addition, the ControlCollection doesn’t have an Exists or a FindControl method.  So, you’re stuck catching exceptions.

foreach (var indicator in _indicators)

{

try

{

var indicatorHeader =

string.Format(“i{0}”, indicator.Id);

((Label)detail.Controls[indicatorHeader]).Text =

IsSpanish                                                                           

? indicator.SpanishText                                                                          : indicator.EnglishText;

}

catch (Exception)

{

continue;

}

}

Unfortunately, that destroys performance—especially with hundreds of iterations (of indicators).

There’s a way around this, at least in my opinion.  Instead of addressing the controls as part of the details.Controls ControlCollection, use a custom IQueryable collection.

Begin by adding a private variable to the report to hold the controls.  In my case, all of the controls I’ll be modifying are Label (of ARControl) controls.

private IQueryable<Label> _controls;

On my ReportStart (I’m sure I could use another method, but since these are static controls, that seemed a good place to start), dump all Label controls into a generic List object (because you cannot add into an IQueryable).

var controlList = new List<Label>();

foreach (var control in detail.Controls.OfType<Label>())

{

controlList.Add(control);

}

_controls = controlList.AsQueryable();

OR, if you want to get fancy and save a few objects:

_controls = detail.Controls.OfType<Label>().AsQueryable();

At this point, your _controls object has all of the standard LINQ goodness.  Our foreach now looks like:

foreach (var indicator in _indicators)

{

var indicatorHeader =

string.Format(“i{0}”, indicator.Id);

var indicatorControl =

_controls.SingleOrDefault(x => x.Name == indicatorHeader);

if (indicatorControl != null)

       {

              indicatorControl.Text = IsSpanish

? indicator.SpanishText

: indicator.EnglishText;

}

}

The entire block can be wrapped in a try/catch—rather than each “object check”.  The report also runs almost 100x faster.