Search This Blog

Loading...

Friday, January 16, 2009

Is LINQ Becoming a Monster?

I love LINQ . It gives the developers the power to query databases and lists in a manner far more succint, readable and elegant than that could otherwise be obtained via traditional looping methods ( you know, the combination kinds of for, if ) that normally result in lines and lines of code, methods and sub-methods calling. LINQ combines the succintness of SQL queries with the compile-time type checking advantage offered by C#, and produces code that is far more intuitive and readable than SQL or C# custom tailored filtering functions.

But after using it for quite sometime, I found that LINQ could be a bit prohibitive. Prohibitive in the sense that you always have to write LINQ expressions that are big, complicated, ugly and undecipherable at times. 

Take a look at the below queries, see how much you can understand out of it?



public DateTime MyDateTime(IEnumerable<timedb.timerecordrow> relatedForm,
DateTime lastDateTime)
{
DateTime dt1 = relatedForm.OrderBy(record => record.EnterTime).FirstOrDefault(record =>
record.EnterTime > lastDateTime).
EnterTime;
DateTime dt2 = relatedForm
.Where(record => !record.IsLeaveTimeNull())
.OrderBy(record => record.LeaveTime)
.FirstOrDefault(record =>record.LeaveTime > lastDateTime).
LeaveTime;

if (dt1 >= dt2)
return dt2;
else
{
return dt1;
}

}
public List<actioninfo> PullLogRecord()
{

var descriptionDB = ActionDescriptionAdapter.GetData();
var timeDB = TimeRecordAdapter.GetData();

var groupByAction = timeDB.GroupBy(e => e.UserActionReference);
var groupByActionAndCurrElement = groupByAction.Select
(
group=>new {Key = descriptionDB
.Where(record=>record.UserAction==group.Key)
.Select(record=>record.Description).Single(),
NestedGroup= group.ToLookup
(
result=>result.CurrElement,
result=>new TimerInformation()
{
EnterTime = result.EnterTime,
LeaveTime = result.IsLeaveTimeNull() ?
MyDateTime(timeDB.ToList(), result.EnterTime) :
result.LeaveTime
}

)}
);
var dictionary = groupByActionAndCurrElement.ToDictionary
(
result => result.Key,
result => result.NestedGroup
);

List<actioninfo> actionInfos=new List<actioninfo>();
foreach (string key in dictionary.Keys)
{
ActionInfo ai = new ActionInfo(){ActionName = key};


foreach (var pair in dictionary[key])
{
ai.ElementInfo.Add(new ElementInfo()
{
ElementName = pair.Key,TimingInfo = pair.ToList()
});
}
actionInfos.Add(ai);

}
return actionInfos;
}


It's awfully long. But what it does is relatively simple:
  1. Pull records from a data table
  2. Group the data in terms of description and CurrElement tables. 
  3. After the grouping,  get each of the EnterTime and LeaveTime for each record and put them in a list, and put the list into an ElementInfo object,  and so on. If the LeaveTime is a null, then it will find the first record that immediately after the corresponding EnterTime

With such a long query, debugging it becomes very difficult. I have tried to break it into smaller methods, but so far only succeeded in spinning off one method. The whole query is still can't fit into a page. It's still a pain to read.

If I were to write my own C# code, although I might need more lines to do it, but at least I could write sub-functions to make things easier to understand. But with LINQ... that's the best I could produce, given my limited time and relatively low exposure in it.

Is there a way to improve on this?