0% found this document useful (0 votes)
43 views

Linq - Beyond Queries: LINQ For Better Business Logic

This document discusses using LINQ features beyond just data queries. It explores using extension methods to build better APIs, lambdas and Func for functional programming, and Expression Trees for static reflection. Examples show building a rules engine by modeling business logic as a flowchart using these LINQ techniques for a more readable and maintainable approach.

Uploaded by

Danish Qureshi
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
43 views

Linq - Beyond Queries: LINQ For Better Business Logic

This document discusses using LINQ features beyond just data queries. It explores using extension methods to build better APIs, lambdas and Func for functional programming, and Expression Trees for static reflection. Examples show building a rules engine by modeling business logic as a flowchart using these LINQ techniques for a more readable and maintainable approach.

Uploaded by

Danish Qureshi
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 15

LINQ – Beyond Queries

LINQ for Better Business Logic


Repurposing LINQ Features

 Extension methods
 For better APIs
 Expression trees
 For static reflection
 Funcs and Actions
 For functional, declarative programming
 Demos
 Functional validation
 Increasingly complex validations
 Building a LINQ powered rules engine
Example Scenario

 Scheduling tasks for periodic execution


public class ScheduledTask {
public ScheduledTask(ITask task,
TimeSpan interval,
TimeSpan expiration) {
Task = task;
Interval = interval;
Expiration = expiration;
}

public ITask Task { get; protected set; }


public TimeSpan Interval { get; protected set; }
public TimeSpan Expiration { get; protected set; }
...
var task = new ScheduledTask(
}
new AccountSynchronizationTask(),
new TimeSpan(0, 0, 2, 0),
new TimeSpan(2, 0, 0, 0));
Goals

 Readability
 Easier to maintain
 Essence over ceremony
 Remove language clutter

var task = new ScheduledTask(


new AccountSynchronizationTask(),
new TimeSpan(0, 0, 2, 0),
new TimeSpan(2, 0, 0, 0));
Named parameters

 Only a small step forward


 Particularly useful when combined with optional parameters
 Gives reader a clue when using constants

var task = new ScheduledTask(


Tasks.AccountSynchronization,
runEvery: new TimeSpan(0, 0, 2, 0),
expiresIn: new TimeSpan(2, 0, 0, 0));
Extension Methods

 Extend types!
 Even sealed types, generic types, and interfaces

public static class StringExtensions


{
public static int ToInt32(this string value)
{
return Int32.Parse(value);
}
}
int value = "32".ToInt32();
Fluent APIs

 A readable API
 Often uses method chaining

var then = 2.Minutes().Ago();

public static TimeSpan Minutes(this int value)


{
return new TimeSpan(0, 0, value, 0, 0);
}

public static DateTime Ago(this TimeSpan value)


{
return DateTime.Now - value;
}
Validation Example

 Dealing with requirements in the form of complex flowcharts


 Model them with procedural if/else code?

public bool IsValid(Movie movie)


{
if(string.IsNullOrEmpty(movie.Title))
{
return false;
}

if(movie.Length < 60 || movie.Length > 400)


{
return false;
}

if(movie.ReleaseDate.Value.Year < 1903)


{
return false;
}

return true;
}
Functional Validation

 Using lambda expressions for a declarative approach


 Keep the code in a data structure for passive evaluation

public bool IsValid(Movie movie)


{
Func<Movie, bool>[] rules =
{
m => string.IsNullOrEmpty(m.Title),
m => m.Length < 60 || m.Length > 400,
m => m.ReleaseDate.Value.Year < 1903
};

return rules.All(rule => rule(movie) == false);

}
A More Complex Scenario

 Instead of validation, we’ll perform a rules evaluation


 Required to compute more than just a binary result
 Should scale up to manage hundreds of rules
 Should be able to complete evaluation even with incomplete data
 Required to provide information about what properties are inspected
Domain Model

 Models the business flowcharts


 Shape, arrows, rules, results

Shape

Shape Shape
Creating the Flowchart

 Tedious!
var chart = new MovieFlowchart();
chart.Shapes.Add(
new Shape<Movie, MovieResult>()
{
Name = "CheckTitle",
Arrows =
{
new Arrow<Movie>
{
PointsTo = "CheckLength",
Rule = m => !String.IsNullOrEmpty(m.Title)
}
},
RequiredField = new PropertySpecifier<Movie>(m=>m.Title)
}
// ... and so on
);
Building the Fluent API / Internal DSL

 Heavy use of extension methods

public static Flowchart<T, R> AddShape<T, R>(


this Flowchart<T, R> chart, string shapeName)
chart.AddShape("CheckTitle")
{
.Requires(m => m.Title)
var shape = new Shape<T, R> { Name = shapeName };
.WithArrowPointingTo("CheckLength").AndRule(TitleNotNullOrEmpty)
chart.Shapes.Add(shape);
.AddShape("CheckLength")
return chart; => m.Length)
.Requires(m
} .WithArrowPointingTo("BadMovie").AndRule(LengthIsTooLong)
.WithArrowPointingTo("GoodMovie").AndRule(LengthIsJustRight)
.WithArrowPointingTo("CheckReleaseDate").AndRule(LengthExists)
public static Flowchart<T, R> YieldsResult<T, R>(
.AddShape("CheckReleaseDate")
this Flowchart<T, R> chart, R result)
.Requires(m => m.ReleaseDate)
{ .WithArrowPointingTo("BadMovie").AndRule(TooOld)
chart.LastShape().Result = result;
.WithArrowPointingTo("GoodMovie").AndRule(HasReleaseDate)
return chart;
.AddShape("BadMovie").YieldsResult(MovieResult.BadMovie)
} .AddShape("GoodMovie").YieldsResult(MovieResult.GoodMovie);
Taking Advantage of Expression<T>

 Expression<T> can yield rich meta-data about a piece of code


 “Static” reflection

public PropertySpecifier(Expression<Func<T, object>> expression)


{
if(expression.Body is MemberExpression)
{
var me = expression.Body as MemberExpression;
_propertyName = me.Member.Name;
}
else if(expression.Body is UnaryExpression)
{
var ue = expression.Body as UnaryExpression;
var me = ue.Operand as MemberExpression;
_propertyName = me.Member.Name;
}
}
Summary

 LINQ features – more than just data access


 Extension methods provide a shim for alternate APIs
 Use lambdas and Func<> for expressive, functional programming
 Leverage Expression<T> for metadata about code

You might also like