The Inquisitive Coder – Davy Brion's Blog

Trying to walk that thin line between intelligence and ignorance

Archive for the 'Software Development' Category

Do You Know What’s Really Going On?

Posted by Davy Brion on 20th January 2010

I recently showed the following picture and asked you what it showed:

Capture

Matt Hidinger pretty much nailed it in the comments:

Well it appears to have something to do with Efferent Coupling at the Type Level (http://www.ndepend.com/Metrics.aspx#TypeCe) — simply meaning the number of types that a class directly depends on.

So it appears that you’re aiming for less than 20 direct type dependencies and highlighting classes with more than 35 deps.

I imagine this is a view of a particular namespace, but I’m not sure on that.

I mentioned in the comments that i’d write more about it soon, so here it is.  That screenshot comes from a product (called Glance) that we initially developed for internal use, because a lot of the people at our company are well, addicted to data.  I guess we got tired of looking at numbers at some point so we decided to spruce things up a little.  Let’s walk through an example, shall we?

In my case, i’m interested in code metrics.  Well, some of them at least.  The following picture shows a high level view on the status (or health) of some code metrics for a particular project:

01

Their names aren’t being shown correctly yet, but the metrics that are shown are the following: Association Between Classes, Cyclomatic Complexity, Depth Of Inheritance, Effererent Coupling and Number Of Lines Of Code.  Notice their colors.  They can range from green (which is good) to red (which is bad).  Again, i’m looking at these metrics on a project level so the colors that are shown here represent the average status of each specific metric for the entire project. In this particular image, you can see that the Cyclomatic Complexity color is starting to turn a bit yellowish.  When you hover over the metric, you get to see the following:

 02

As the description of this particular metric says, the goal is to get a value of 0.2 which would result in a status of +1.  0.4 would be neutral so that’s 0 and anything greater than 0.6 would be –1.  The color ranges from red to green based on the status (-1 = red, +1 = green). For this particular project, the average value of all types is 0.5649This is sort of confusing, because you’d think that it should be close to red but the average status is 0.3587 and that is what the color is based on.  Now, if the average value is close to red yet the average status is green, it means that there are probably some classes that have a very high score (for this metric, that’s obviously very bad) and they have a heavy influence on the average.  The average status however, is not impacted in this case.  At this point, we’re still not quite sure whether the color should be based on the average status, or the average value but that’s a minor detail which isn’t important for this post :)

Anyway, when i click on the metric i get the following screen:

03

This shows a circle for each top-level namespace in the project.  Each circle is colored based on the average status of the types within that namespace.  As you can see, some of them are green, and some are yellow.  What you don’t see in this screenshot is that those circles are all floating around the center circle (which represents the parent level of this view) in a 3D view.  I rotated the view to make it as clear as possible where the circles are located.  When you hover over one of the circles, you’ll again see the value and the status of the namespace that is represented by that circle.

04

Obviously, you can do this with each circle in the view:

 05

This namespace isn’t even close to green, so we really ought to check out what’s going on.  When i click on it, i drill-down into the data again, and i get to see the namespaces and classes that are in the EMS.Silverlight.Common namespace.

06

The EMS.Silverlight.Common.Controls.Tooltip namespace seems to be in the worst shape, so i click on it again.

 07

Oops.  We have two classes that are pretty red.  I guess we’ve identified a good candidate for refactoring :)

 08

To wrap up the screenshot bonanza i’ve got going on here, here’s a picture of the Lines Of Code metric in a large web application.  It clearly shows that there are 4 parts that we really need to look at for large classes, while the rest of it doesn’t really warrant any attention at this time.

09

Now, as cool as those screenshots are (at least, i certainly think so) they aren’t really the coolest part of all this.  First of all, you might be wondering “who on earth goes to all this trouble to develop a tool to look at code metrics like this?”.  Well, as a company we are kinda “out there” but we’re definitely not that far “out there”.  It would be ridiculous to develop something like this purely to look at code metrics.

Instead, we can use this for pretty much any kind of data.  The tool doesn’t even know it’s showing code metrics.  It is just showing data from a data warehouse where we’ve defined dimensions and hierarchies, like Assembly -> Namespace -> Type in the above example, to define the context for some Key Performance Indicators (KPI’s).  The actual code metrics come from nightly NDepend builds that we run for each of our projects.  The data is imported in our warehouse and then we can look at it with this tool due to the KPI’s that we’ve defined.

So basically, pretty much everything is possible as long as you can get the data in your warehouse and define some KPI’s for that data.  I only showed you code metrics in this post, but we use it for a lot more than that.   Some examples include Holiday Acceptance (which manager is the fastest or slowest in approving holidays), Aging Invoices (which customers are always late with payments), Project Revenue (which projects do we make the most money on), Issues (who fixes bugs the fastest? who creates the most or the fewest bugs?), etc…

And you really can get all of that data from everywhere… in our case, we get a lot of data from our EMS system and obviously from Genesis as well.  We also get data from NDepend (as you’ve seen in this post), from TeamCity (anything build-related), Subversion, whatever you want basically.  Obviously, you’re not limited to looking at the data in the ‘circle view’.  You can get traditional graphs as well.  For instance:

image

This is global view of all requested changes for all of our projects (both commercial projects for customers as our internal applications).

The following picture shows the average lead time for issues that we had in 2009 based on the type of issue:

image

Finally, an overview of the time we spend on global task types:

image

Note that we do very little up-front design, and our continuous/evolutionary design is obviously a part of our regular Development tasks.  And i’m sure you can understand that this kind of data is very helpful when we need to make estimations for new projects.  We have a very good view on the amount of overhead that we typically have for project management, for instance.

One of the biggest benefits (IMO anyway) is that with the way the data is visualized (especially with the circle view), everyone can easily look at the data and see where more attention is required.  I don’t know a lot about finances but i know that red circles are bad.  My boss doesn’t know anything about good code, but even he now has a pretty good view on the quality of our code and he actually likes coming up to some of our guys to say “wow, class X in your project really could use some attention huh?”.

And today i heard that one of our customers who’s already using this is apparently learning quite a lot about their business because of this tool.  They also put a lot of data in their warehouse and they are learning in advance where they need to improve their business processes before those things really become a problem because of what Glance is showing them.  The process is actually pretty simple. If it’s green, you don’t really need to spend time on it.  If it’s turning yellow or red, you probably want to investigate things and try to improve the situation before it really starts causing problems.  In the future, we’re also going to add automated notifications to it.  So once things start going bad, you’ll be notified of them automagically.

In short, you can find out what’s really going on in your organization and focus your efforts on where it truly makes sense to do so.  And that, my friends, is what it’s all about.

Posted in Software Development | 12 Comments »

Database Audit Trails: How Would You Do It?

Posted by Davy Brion on 19th October 2009

For those of you wondering what the background story on the previous post is about: we had a meeting today to discuss 2 alternatives on how we should implement the database audit trail for a new project we’re doing. These are the functional requirements:

  • We have to be able to easily retrieve what user X changed in the database on day Y or during a certain time frame.
  • We need to know what record X looked like at point Y in time, and be able to show a historic list of changes for a specific record.
  • We need to configure the duration for which auditing data must be kept in the database on a per-table (or per-schema) level

We’ve done this in the past, but never in a manner that is just perfect (or close enough to being perfect anyway) for both requirements. So we discussed two alternative approaches to tackling this problem.

The first approach is to have 3 tables. The first two tables would contain metadata about the database structure (tables and columns basically). The third table would have a reference to each column with a previous value, the new value, who changed it and a timestamp of when the change occurred. We would use database triggers that would store the appropriate auditing data in the third table for each action (insert/update/delete). The benefits of this approach would be that it would be pretty easy to set up and also easy to query which changes were made by a certain user. It would be a bit more complex to reconstruct the state of a record at a certain point in time, but it would still be doable. The biggest downside (in my opinion), is that for each insert/update you would have X amount of inserts into the auditing table, and you’d also have to either perform some queries to get the identifier values of the relevant tables/columns or hardcode those identifier values in the generated triggers to avoid the cost of the extra select statements. So if you have an entity with 12 columns, an insert into the entity’s table would mean 12 inserts into the auditing table. Updating 4 columns of that entity would lead to 4 insert statements in the auditing table, etc…

The other alternative that we considered was to use a specific auditing table for each real table, and every time a user does an insert/update/delete, insert one record into the table’s specific auditing table with the current state of the record along with a timestamp, the userid, and an indication of which action was performed (insert/update/delete). This insert would also be done with database triggers. The benefits of this approach would be that it would be very easy to show what the state of a certain entity was at a certain point in time, and that you’d only pay the cost of the single extra insert. There are however two big downsides to this approach. The first is that it’ll lead to an explosion of tables. We’d probably store the auditing tables in a separate database but you still basically have twice the amount of tables (and scripts!) that you’d have to worry about compared to what you’d usually have. We can very realistically expect a couple hundred of real tables in a few years, so you’d have to multiply that by two if you’d go for this auditing approach. Another big downside is that it would be very expensive to perform a query which would tell you what user X changed at point Y in time (or during a time frame).

Neither solution appears to be ideal. Also, this database will definitely grow a lot (more tables and obviously data) as we add more functionality and it’s pretty safe to consider this entire project as something that will be expanding in functionality for a couple of years to come.

So my question to you is: Which approach do you think is best, and why? Or better yet, is there a better approach that we simply haven’t thought of yet? I would definitely appreciate some feedback on this (either on the validity of both approaches or alternative approaches) so please do feel free to leave your thoughts in the comments :)

Posted in Software Development | 20 Comments »

Slutty Types

Posted by Davy Brion on 11th October 2009

Slutty Types are types which:

  • give you access to their privates without too many difficulties
  • don’t really care about your intentions, or if they do, aren’t very clear on that
  • occasionally seem like a good short-term fix
  • can be used in a variety of ways, with different outcomes and none of them are guaranteed
  • can not to be trusted
  • really need to be tested
  • will burn you sooner or later if you’re not careful
  • become even more of a mess during the aging process

Feel free to add to the list :)

Posted in Software Development | 23 Comments »

What Kind Of Developer Are You?

Posted by Davy Brion on 27th September 2009

It occurred to me recently how much i’ve changed as a developer in the past few years. When i first started working as a developer, i was pretty quick at adding new features to projects and fixing bugs. I was almost fully focused on adding functionality but i didn’t really spend a lot of time thinking about how i was adding the functionality. In some cases, i complained when i felt that some of our approaches could be better and in other cases i came up with alternative approaches but generally speaking, i was in full “i’m new at this, i’m learning and i’m just trying to do the best i can”-mode. For me, that meant trying to complete your assignments within (or ahead of) time, and with as few issues/bugs as possible.

This worked pretty well for the first 2 years or so of my career, but a lot of that changed when i started working on our own little ORM in VB6 together with a more experienced coworker. For some reason, writing code that made the job of developers easier somehow was more appealing to me than writing code that made the job of our users easier. From that point on, my focus gradually shifted from being a developer who tries to add value through functionality to being a developer who tries to add value by reducing complexity. With that i mean that i spent less time working on actual features that our users would benefit from, and more time on writing code that other developers would benefit from. And that, in turn, would (should?) offer more benefit to our users on a larger scale because my code was supposed to make it easier for other developers to add new functionality.

Just to be clear on this: i think we need both ‘kinds’ (yes, i know there are more ‘kinds’ of developers but bear with me on this) of developers. If a team of developers consists solely of people like me, you’re never going to deliver anything of value to your users because we would spend all of our time trying to improve whatever it is that we’re doing. Just like you need people who are willing to think a bit further about how you’re doing things, you need people who manage to actually get things done in a manner that is good from both the technical as well as the end-user point of view.

Sadly, i’m no longer one of those people. I used to be, but i don’t really think i’m capable of going back to that. To me, providing a good User Experience means coming up with a good API. So i spend most of my time working on architectural and framework stuff. One of my biggest fears is that i’ll some day end up as the typical non-coding architect so i try to stay very involved with how people are actually writing code to deliver real functionality. I may not write that kind of code anymore, but i do keep looking at a lot of that code so i can keep coming up with possible improvements.

So what about you? What are the things you focus most on, as a developer, and why? What are the things that get you the most satisfaction (keep this technical, i don’t wanna know about your personal life) and are you able to focus most of your energy on this?

Posted in Software Development | 5 Comments »

Surface Development For Belgian TV

Posted by Davy Brion on 22nd June 2009

Some of my coworkers recently developed 2 Surface applications for VTM, one of the commercial Belgian TV channels. This is one of the few high-profile usages of Microsoft’s Surface in Belgium so far. Be sure to check out the videos on our company website.

Posted in Software Development | No Comments »

Why Are Mutable Value Types Even Possible?

Posted by Davy Brion on 17th May 2009

A coworker of mine was recently trying to figure out why some code wouldn’t compile.

Consider the following code:

            var point = new System.Windows.Point(5, 7);

            point.X = 6;

            point.Y = 8;

            Assert.AreEqual(6, point.X);

            Assert.AreEqual(8, point.Y);

This compiles and works. Now consider the following class:

    public class SomeClass

    {

        public System.Windows.Point SomePoint { get; set; }

    }

And then this small piece of code:

            var myObject = new SomeClass

            {

                SomePoint = new Point(2, 3)

            };

 

            myObject.SomePoint.X = 5;

You’d typically expect that this code would compile, right? Well it doesn’t. It fails with the following compiler error:

error CS1612: Cannot modify the return value of ‘SomeApp.SomeClass.SomePoint’ because it is not a variable

Not exactly what you would expect, right? Well, the compiler error in this case is actually a good thing, though the message isn’t as clear as it should be. See, System.Windows.Point is actually a value type instead of a reference type, and as you should know, value types behave very differently from reference types when passed around. You basically get a copy every time instead of the actual instance.

Let me just borrow the explanation of the “DO NOT define mutable value types” guideline, from Microsoft’s Framework Design Guidelines:

Mutable value types have several problems. For example, when a property getter returns a value type, the caller receives a copy. Because the copy is created implicitly, developers might not be aware that they are mutating the copy, and not the original value.

Now the compiler error makes sense, right? Well, the message still sucks, but it makes sense that the compiler would protect you from making this mistake.

It won’t protect you from the following mistake though:

            var myObject = new SomeClass

            {

                SomePoint = new Point(2, 3)

            };

 

            var point = myObject.SomePoint;

            point.X = 5;

This code will compile, though if you expect that myObject.SomePoint.X will return 5, you’re in for a treat. Hopefully nobody writes code like this, but imagine having to debug something like this in some legacy code that was never properly tested.

But really, why are mutable value types even possible? In what situation does a mutable value type make sense? I’d really like to know, so please point out those situations in the comments if you know of one. The only reason i can think of is performance but the number of situations where a mutable value type is going to give you a significant performance boost is probably so small that it would’ve made more sense to enable mutability for value types with a language keyword or something like that. Unless there is another benefit that i’m not aware of yet?

Posted in Software Development | 4 Comments »

Who’s In Charge Of The Time?

Posted by Davy Brion on 6th May 2009

I recently noticed that 2 tests were suddenly failing for no apparent reason. This was in a large codebase where we have a mix of two development approaches. The project is simply too big to convert everything at once to the latest development approach so we’re using Ayende’s Obsolete In Isolation approach on this. The 2 failing tests were in the old parts of the code, so i cringed and then set out to investigate.

The tests of the old part of the code aren’t always easy to comprehend and they use the database all over the place. I figured i’d quickly run them on the database that we use when we run those tests on our local machines, and after that, run them on the database that the buildserver uses (which is rebuilt entirely with each build) to examine the differences between both test runs. They both passed in both cases. They were still failing on the buildserver though.

Upon first inspection of the code of those tests, it didn’t seem like there was any blatantly obvious reason why those tests were failing on one machine, and not on the others. They were inserting some records with some manipulated DateTime values, and then calling some code which would execute a query which should have retrieved these values. The records inserted by the test were not retrieved for some reason, which caused the tests to fail.

DateTime values and tests can be tricky sometimes so i started looking in that direction. The queries that where executed in those tests would use the current date/time together with a margin of a couple of minutes to retrieve records where a certain date/time column’s value would fall in the range of the current date/time with the given margin. The records that were inserted in the tests would use the current date/time, minus 2 minutes, or plus 2 minutes. Hmm… this could possibly be flaky depending on how the query determines the current date/time. If the query receives the current date/time from the calling code, then this wouldn’t cause problems but if it uses the current date/time of the database, then this is not only a flaky test, but a real bug in the code.

I then checked the system clock of the buildserver, and as i suspected, it was about 4 minutes ahead of the system clock of the database server. Normally, all of our servers’ system clocks are synchronized, but for some reason, the buildserver hadn’t had its system clock synchronized since the day before. The code was inserting records in the database with modified date/time values, based on the system clock of the system the code was running on, and was then querying the database using the database’s system clock. Since the time difference was big enough, the query would not return the expected rows and this made the tests fail on the buildserver.

Obviously, this never would’ve happened if the query would’ve used a parameter supplied by the caller which would’ve represented the current date. And that is the real bug that these flaky tests (which up until this particular incident never failed) highlighted. A situation like this could’ve occurred in production if the application server’s clock and the database server’s clock would’ve gotten out of synch. Can you imagine the pain you’d go through when investigating a bug report like this? :p

Date/Time values are often tricky, especially when there is more than one source which could determine the current Date/Time. So do yourself, and anyone who will maintain your code down the line, a favor by making sure that only ONE source is in charge of Date/Time values in your system. Using the database for all date/time values is definitely not always possible, so it’s best to have your application server be in charge of all date/time values. If you need the current date/time in a query, simply supply it as a parameter to the query and you’ll avoid problems like this.

Though this advice does leave me wondering what the best approach to deal with this is when you have multiple application servers which could assign date/time values.

Posted in Software Development | 3 Comments »

Help Us Name This Method

Posted by Davy Brion on 24th April 2009

This worked pretty good last time so i’m gonna try it again. We have a Repository base class (yes we still use it, and no, i’m not going to get into the discussion that’s going on about in on other blogs) which uses NHibernate underneath. We have the typical Get method which retrieves an object based on an ID. This method uses NHibernate’s ISession’s Get method, which either returns the entity if it’s present in the database, or it returns null.

Some people want to put an equivalent to NHibernate’s ISession’s Load method into the base repository class, but we just can’t come up with a good name for it. In case you don’t know, ISession.Load either returns the actual entity if it’s already in the session cache, or it returns a proxy for the entity but it does not immediately hit the database. The functionality of the Load method is thus very different from the Get method. Now, i think this is a case of extremely bad naming within NHibernate, and i don’t want to have the same naming issue in our Repository implementation.

So basically, the question is: how would you name the equivalent of the Load method? Remember: this method returns a proxy to an entity if it’s not yet in the session cache, whereas the Get method actually retrieves the entity from the database immediately or returns null if the entity for the given ID does not exist.

Posted in Software Development | 23 Comments »

Small Milestone

Posted by Davy Brion on 21st April 2009

subscriber_count

Finally ;)

Posted in Software Development | 4 Comments »

Gotta Love Refactoring

Posted by Davy Brion on 19th April 2009

Behold the following two classes:

    public class FutureCriteriaBatch

    {

        private readonly List<ICriteria> criterias = new List<ICriteria>();

        private readonly IList<System.Type> resultCollectionGenericType = new List<System.Type>();

 

        private int index;

        private IList results;

        private readonly ISession session;

 

        public FutureCriteriaBatch(ISession session)

        {

            this.session = session;

        }

 

        public IList Results

        {

            get

            {

                if (results == null)

                {

                    var multiCriteria = session.CreateMultiCriteria();

                    for (int i = 0; i < criterias.Count; i++)

                    {

                        multiCriteria.Add(resultCollectionGenericType[i], criterias[i]);

                    }

                    results = multiCriteria.List();

                    ((SessionImpl)session).FutureCriteriaBatch = null;

                }

                return results;

            }

        }

 

        public void Add<T>(ICriteria criteria)

        {

            criterias.Add(criteria);

            resultCollectionGenericType.Add(typeof(T));

            index = criterias.Count - 1;

        }

 

        public void Add(ICriteria criteria)

        {

            Add<object>(criteria);

        }

 

        public IFutureValue<T> GetFutureValue<T>()

        {

            int currentIndex = index;

            return new FutureValue<T>(() => (IList<T>)Results[currentIndex]);

        }

 

        public IEnumerable<T> GetEnumerator<T>()

        {

            int currentIndex = index;

            return new DelayedEnumerator<T>(() => (IList<T>)Results[currentIndex]);

        }

    }

and

    public class FutureQueryBatch

    {

        private readonly List<IQuery> queries = new List<IQuery>();

        private readonly IList<System.Type> resultCollectionGenericType = new List<System.Type>();

 

        private int index;

        private IList results;

        private readonly ISession session;

 

        public FutureQueryBatch(ISession session)

        {

            this.session = session;

        }

 

        public IList Results

        {

            get

            {

                if (results == null)

                {

                    var multiQuery = session.CreateMultiQuery();

                    for (int i = 0; i < queries.Count; i++)

                    {

                        multiQuery.Add(resultCollectionGenericType[i], queries[i]);

                    }

                    results = multiQuery.List();

                    ((SessionImpl)session).FutureQueryBatch = null;

                }

                return results;

            }

        }

 

        public void Add<T>(IQuery query)

        {

            queries.Add(query);

            resultCollectionGenericType.Add(typeof(T));

            index = queries.Count - 1;

        }

 

        public void Add(IQuery query)

        {

            Add<object>(query);

        }

 

        public IFutureValue<T> GetFutureValue<T>()

        {

            int currentIndex = index;

            return new FutureValue<T>(() => (IList<T>)Results[currentIndex]);

        }

 

        public IEnumerable<T> GetEnumerator<T>()

        {

            int currentIndex = index;

            return new DelayedEnumerator<T>(() => (IList<T>)Results[currentIndex]);

        }

    }

They are almost exactly the same, which is obviously a problem. The root cause of this situation is that there is no common interface between IQuery and ICriteria, and there also isn’t a common interface between IMultiQuery and IMultiCriteria. Introducing those interfaces would make a lot of things easier, but can’t be done right now. Still, that shouldn’t prevent us from striving to avoid duplicate code.

This is actually pretty easy to do… here’s the new FutureBatch class:

    public abstract class FutureBatch<TQueryApproach, TMultiApproach>

    {

        private readonly List<TQueryApproach> queries = new List<TQueryApproach>();

        private readonly IList<System.Type> resultTypes = new List<System.Type>();

        private int index;

        private IList results;

 

        protected readonly SessionImpl session;

 

        protected FutureBatch(SessionImpl session)

        {

            this.session = session;

        }

 

        public IList Results

        {

            get

            {

                if (results == null)

                {

                    GetResults();

                }

                return results;

            }

        }

 

        public void Add<TResult>(TQueryApproach query)

        {

            queries.Add(query);

            resultTypes.Add(typeof(TResult));

            index = queries.Count - 1;

        }

 

        public void Add(TQueryApproach query)

        {

            Add<object>(query);

        }

 

        public IFutureValue<TResult> GetFutureValue<TResult>()

        {

            int currentIndex = index;

            return new FutureValue<TResult>(() => GetCurrentResult<TResult>(currentIndex));

        }

 

        public IEnumerable<TResult> GetEnumerator<TResult>()

        {

            int currentIndex = index;

            return new DelayedEnumerator<TResult>(() => GetCurrentResult<TResult>(currentIndex));

        }

 

        private void GetResults()

        {

            var multiApproach = CreateMultiApproach();

            for (int i = 0; i < queries.Count; i++)

            {

                AddTo(multiApproach, queries[i], resultTypes[i]);

            }

            results = GetResultsFrom(multiApproach);

            ClearCurrentFutureBatch();

        }

 

        private IList<TResult> GetCurrentResult<TResult>(int currentIndex)

        {

            return (IList<TResult>)Results[currentIndex];

        }

 

        protected abstract TMultiApproach CreateMultiApproach();

        protected abstract void AddTo(TMultiApproach multiApproach, TQueryApproach query, System.Type resultType);

        protected abstract IList GetResultsFrom(TMultiApproach multiApproach);

        protected abstract void ClearCurrentFutureBatch();

    }

And now the original 2 classes look like this:

    public class FutureCriteriaBatch : FutureBatch<ICriteria, IMultiCriteria>

    {

        public FutureCriteriaBatch(SessionImpl session) : base(session) {}

 

        protected override IMultiCriteria CreateMultiApproach()

        {

            return session.CreateMultiCriteria();

        }

 

        protected override void AddTo(IMultiCriteria multiApproach, ICriteria query, System.Type resultType)

        {

            multiApproach.Add(resultType, query);

        }

 

        protected override IList GetResultsFrom(IMultiCriteria multiApproach)

        {

            return multiApproach.List();

        }

 

        protected override void ClearCurrentFutureBatch()

        {

            session.FutureCriteriaBatch = null;

        }

    }

and

    public class FutureQueryBatch : FutureBatch<IQuery, IMultiQuery>

    {

        public FutureQueryBatch(SessionImpl session) : base(session) {}

 

        protected override IMultiQuery CreateMultiApproach()

        {

            return session.CreateMultiQuery();

        }

 

        protected override void AddTo(IMultiQuery multiApproach, IQuery query, System.Type resultType)

        {

            multiApproach.Add(resultType, query);

        }

 

        protected override IList GetResultsFrom(IMultiQuery multiApproach)

        {

            return multiApproach.List();

        }

 

        protected override void ClearCurrentFutureBatch()

        {

            session.FutureQueryBatch = null;

        }

    }

They are still very similar, but at least it’s a big improvement over the original situation.

Posted in Software Development | 1 Comment »