SlideShare ist ein Scribd-Unternehmen logo
1 von 90
-And look what you've done to mother! She's worn out fiddling 
with all your proxy classes. 
- There's nowt wrong wi' proxy classes, lad! I've generated more 
proxy classes than you've had hot dinners!
dynamic client = WeirdWildStuffFactory.GiveMeOneOfThose(); 
client.NowICanPretendAnySillySentenceIsAMethodCall(); 
client.AndICanSendAnyArguments(1, "2", new Stream[] {}); 
var result = client.MeaningOfLife * 42 * Guid.NewGuid(); 
Assert.AreEqual(42, result);
// Without dynamic binding 
((Excel.Range)excelApp.Cells[1,1]).Value2 = "Name"; 
var range2008 = (Excel.Range)excelApp.Cells[1,1]; 
// With dynamic binding 
excelApp.Cells[1,1].Value = "Name"; 
var range2010 = excelApp.Cells[1,1];
// With Entity Framework 
public User FindUserByEmail(string email) 
{ 
return _context 
.Users.Where(x => x.Email == email) 
.FirstOrDefault(); 
} 
// With Simple.Data 
public User FindUserByEmail(string email) 
{ 
return Database.Open() 
.Users.FindAllByEmail(email) 
.FirstOrDefault(); 
}
Background 
Given 
Name Birth date Height 
John 1940-10-09 1.80 
Paul 1942-06-18 1.80 
George 1943-02-25 1.77 
Ringo 1940-07-07 1.68 
[Given(@"the following users exist in the database:")] 
public void GivenTheFollowingUsersExist(Table table) 
{ 
IEnumerable<dynamic> users = table.CreateDynamicSet(); 
db.Users.Insert(users); 
}
• This talk is not about making a choice on your behalf
• In fact, this talk is about leaving you multiple choices
• Moreover, this talk is about giving you an opportunity to change 
your mind later with minimum effort
• This talk is about creating libraries that would expose both typed 
and dynamic API – but not as two different APIs
•We will show how to make a single API that can be called from 
either static typed or dynamic client
• Our hybrid API will have hybrid packaging – it will disable 
dynamic support when installed on platforms that lack runtime 
method binding
dynamic results = db.Companies 
.FindAllByCountry("Norway") 
.FirstOrDefault(); // EXCEPTION
var result = db.Companies 
.Where(x => x.CompanyName == "DynamicSoft") 
.Select( x => 
new 
{ 
c.CompanyName, 
c.YearEstablished 
});
var result = db.Companies 
.Where(x => x.CompanyName == "DynamicSoft") 
.Select( x => 
new 
{ 
c.CompanyName, 
c.YearEstablished 
}); 
var result = db.Companies 
.FindByCompanyName("DynamicSoft") 
.SelectCompanyNameAndYearEstablished();
var result = db.Companies 
.Where(x => x.CompanyName == "DynamicSoft") 
.Select( x => 
new 
{ 
c.CompanyName, 
c.YearEstablished 
}); 
dynamic x = new DynamicQueryExpression(); 
var result = db.Companies 
.Where(x.CompanyName == "DynamicSoft") 
.Select(x.CompanyName, x.YearEstablished);
• My API is my castle, any naming scheme is a matter of a personal 
preference
• So we will leave method naming to personal opinion and ask a 
different question
• Do you want to publish one API or two APIs?
interface IQueryBuilder 
{ 
From(...); 
Where(...); 
OrderBy(...); 
OrderByDescending(...); 
Select(...); 
} 
• This interface defines core operation set of our internal DSL 
and is shared by static typed and dynamic clients 
• Typed and dynamic clients differ in what kind of arguments 
they send to the operations: static typed or dynamic 
respectively
var result = db.Companies 
.Where(x => x.CompanyName > "D") 
.OrderBy(x => x.CompanyName); 
dynamic x = new DynamicQueryExpression(); 
var result = db.Companies 
.Where(x.CompanyName > "D") 
.OrderBy(x.CompanyName); 
• Exposing single API with two similar looking parameter syntax 
flavors unifies API operations across paradigms 
• API users learn the same API operations no matter what 
paradigm they choose 
• API users can switch paradigms at relatively low costs
LINQ expressions 
custom expression 
LINQ expression 
custom expression 
custom expression 
custom expression 
IDynamicMetaObjectProvider
Finder.dll 
public interface IFinder 
{ 
public class DynamicQueryExpression 
: QueryExpression, IDynamicMetaObjectProvider 
{ 
public DynamicQueryExpression() {} 
} 
Result Find<T>(Expression<Func<T>,bool>> query); 
Result Find(QueryExpression query); 
} 
public class QueryExpression 
{ 
// no public constructor 
} 
Finder.Dynamic.dll
Static typed client 
var finder = new Finder(); 
var result = finder.Find<Companies>().Where(x => 
x.CompanyName.StartsWith("D") && 
x.YearEstablished > 2000); 
dynamic x = new DynamicQueryExpression(); 
var finder = new Finder(); 
var result = finder.Find(x.Companies).Where( 
x.CompanyName.StartsWith("D") && 
x.YearEstablished > 2000); 
Dynamic client
var result1 = from c in db.Companies 
where c.CompanyName.StartsWith("D") 
orderby c.CompanyName 
select new 
{ 
c.CompanyName, 
c.YearEstablished 
}; 
var result2 = db.Companies 
.Where(x => x.CompanyName.StartsWith("D") 
.OrderBy(x => x.CompanyName) 
.Select( x => 
new 
{ 
c.CompanyName, 
c.YearEstablished 
});
interface ICommandBuilder 
{ 
From(...) 
Where(...) 
OrderBy(...) 
OrderByDescending(...) 
Select(...) 
}
interface ICommandBuilder 
{ 
ICommandBuilder From(string tableName); 
ICommandBuilder Where(string condition); 
ICommandBuilder OrderBy(params string[] columns); 
ICommandBuilder OrderByDescending(params string[] columns); 
ICommandBuilder Select(params string[] columns); 
Command Build(); 
} 
// Usage example 
var command = new CommandBuilder() 
.From("Companies") 
.Where("YearEstablished>2000 AND NumberOfEmployees<100") 
.OrderBy("Country") 
.Select("CompanyName", "Country", "City") 
.Build();
public class Command 
{ 
private string _table; 
private string _where; 
private List<string> _selectColumns; 
private List<KeyValuePair<string, bool>> _orderByColumns; 
... 
}
private string Format() 
{ 
var builder = new StringBuilder(); 
builder.AppendFormat("SELECT {0} FROM {1}", 
_selectColumns.Any() ? 
string.Join(",", _selectColumns) : "*", _table); 
if (!string.IsNullOrEmpty(_where)) 
builder.AppendFormat(" WHERE {0}", _where); 
if (_orderByColumns.Any()) { 
builder.AppendFormat(" ORDER BY {0}", 
string.Join(",", _orderByColumns.Select( 
x => x.Key + (x.Value ? " DESC" : string.Empty)))); 
} 
return builder.ToString(); 
}
interface ICommandBuilder 
{ 
ICommandBuilder<T> From<T>(); 
} 
interface ICommandBuilder<T> 
{ 
ICommandBuilder<T> Where( 
Expression<Func<T, bool>> expression); 
ICommandBuilder<T> OrderBy( 
Expression<Func<T, object>> expression); 
ICommandBuilder<T> OrderByDescending( 
Expression<Func<T, object>> expression); 
ICommandBuilder<T> Select( 
Expression<Func<T, object>> expression); 
Command Build(); 
}
var command = new CommandBuilder() 
.From<Companies>() 
.Where(x => 
x.YearEstablished > 2000 && 
x.NumberOfEmployees < 100) 
.OrderBy(x => 
x.Country) 
.Select(x => new { 
x.CompanyName, 
x.Country, 
x.City }) 
.Build();
Nobody expects 
to parse LINQ 
expression trees!
var command = new CommandBuilder() 
.From<Companies>() 
.Where(x => 
x.YearEstablished > 2000 && 
x.NumberOfEmployees < 100) 
.OrderBy(x => 
x.Country) 
.Select(x => new { 
x.CompanyName, 
x.Country, 
x.City }) 
.Build();
Expression<Func<Companies, bool>> expression = x => 
x.YearEstablished > 2000 && x.NumberOfEmployees < 100; 
Lambda 
Left Right 
Left Right Left Right 
AndAlso 
GreaterThan 
Member 
Access Constant 
Member Value Member Value 
2000 
Constant 
100 
LessThan 
Member 
Access 
Body 
Year 
Established 
Number 
OfEmployees 
Int32 Int32
builder.From<Table>() 
command. 
Where(x => expression<Func>T, bool>> 
CommandExpression. 
FromLinqExpression(expression.Body) 
builder.Build() 
Assigns the type 
Assigns Where 
expression 
Converts to 
custom expression 
Evaluates 
the expression
• Defined ICommandBuilder and ICommandBuilder<T> interfaces 
• Implemented CommandBuilder 
• Implemented CommandExpression 
• LINQ expression parsing 
• Expression evaluation
interface ICommandBuilder 
{ 
ICommandBuilder<T> From<T>(); 
} 
interface ICommandBuilder<T> 
{ 
ICommandBuilder<T> Where( 
Expression<Func<T, bool>> expression); 
ICommandBuilder<T> OrderBy( 
Expression<Func<T, object>> expression); 
ICommandBuilder<T> OrderByDescending( 
Expression<Func<T, object>> expression); 
ICommandBuilder<T> Select( 
Expression<Func<T, object>> expression); 
Command Build(); 
}
var result = db.From<Companies>() 
.Where(x => x.CompanyName == "DynamicSoft") 
.Select( x => 
new 
{ 
c.CompanyName, 
c.YearEstablished 
}); 
dynamic x = new DynamicQueryExpression(); 
var result = db.From(x.Companies) 
.Where(x.CompanyName == "DynamicSoft") 
.Select(x.CompanyName, x.YearEstablished);
var result = db.From<Companies>() 
.Where(x => x.CompanyName == "DynamicSoft") 
.Select( x => 
new 
{ 
c.CompanyName, 
c.YearEstablished 
}); 
dynamic x = new DynamicQueryExpression(); 
var result = db.From(x.Companies) 
.Where(x.CompanyName == "DynamicSoft") 
.Select(x.CompanyName, x.YearEstablished);
• Core API is static typed and packaged in assembly that doesn’t 
reference types from System.Dynamic namespace 
• API exposes a DSL based on LINQ expressions 
custom expression 
LINQ expression 
custom expression 
custom expression 
custom expression 
IDynamicMetaObjectProvider 
• Dynamic extensions for the API are packaged in a different 
assembly that is deployed on platforms with DLR support
interface ICommandBuilder 
{ 
ICommandBuilder<T> From<T>(); 
ICommandBuilder<object> From<T>( 
CommandExpression expression); 
}
interface ICommandBuilder<T> 
{ 
ICommandBuilder<T> Where( 
Expression<Func<T, bool>> expression); 
ICommandBuilder<T> Where(CommandExpression expression); 
ICommandBuilder<T> OrderBy( 
Expression<Func<T, object>> expression); 
ICommandBuilder<T> OrderBy( 
params CommandExpression[] columns); 
ICommandBuilder<T> OrderByDescending( 
Expression<Func<T, object>> expression); 
ICommandBuilder<T> OrderByDescending( 
params CommandExpression[] columns); 
ICommandBuilder<T> Select( 
Expression<Func<T, object>> expression); 
ICommandBuilder<T> Select( 
params CommandExpression[] columns); 
Command Build(); 
}
interface ICommandBuilder<T> 
{ 
... 
ICommandBuilder<T> Where( 
Expression<Func<T, bool>> expression); 
ICommandBuilder<T> Where( 
CommandExpression expression); 
... 
} 
// Typed client 
builder.Where(x => x.CompanyName == "DynamicSoft"); 
// Dynamic client 
x = new DynamicCommandExpression() 
builder.Where(x.DynamicName == "DynamicSoft");
interface ICommandBuilder<T> 
{ 
... 
ICommandBuilder<T> OrderBy( 
Expression<Func<T, object>> expression); 
ICommandBuilder<T> OrderBy( 
params CommandExpression[] columns); 
... 
} 
// Typed client 
builder.OrderBy(x => x.CompanyName); 
// Dynamic client 
x = new DynamicCommandExpression() 
builder.OrderBy(x.CompanyName);
interface ICommandBuilder<T> 
{ 
... 
ICommandBuilder<T> Select( 
Expression<Func<T, object>> expression); 
ICommandBuilder<T> Select( 
params CommandExpression[] columns); 
... 
} 
// Typed client 
builder.Select(x => new { x.Country, x.CompanyName }); 
// Dynamic client 
x = new DynamicCommandExpression() 
builder.Select(x.Country, x.CompanyName);
public ICommandBuilder<T> Where( 
Expression<Func<T, bool>> expression) 
{ 
_command.Where( 
CommandExpression.FromLinqExpression(expression.Body)); 
return this; 
} 
public ICommandBuilder<T> Where( 
CommandExpression expression) 
{ 
_command.Where(expression); 
return this; 
}
DynamicObject 
CommandExpression 
IDynamicMetaObjectProvider 
CommandExpression 
DynamicCommandExpression 
DynamicMetaObject 
• DynamicMetaObject
Where(dynamic expression) 
Where(CommandExpression) 
DynamicCommandExpression 
to CommandExpression 
builder.Where(expression).Build() 
Call with dynamic 
argument 
Finds suitable 
method overload 
Converts to 
typed expression 
Follow typed 
builder workflow
input 
return 
CommandProcessor
// Typed 
var command = commandBuilder 
.From<Companies>() 
.Build(); 
var commandProcessor = new CommandProcessor(command); 
var result = commandProcessor.FindOne<Companies>(); 
// Dynamic 
var x = new DynamicCommandExpression(); 
var command = commandBuilder 
.From(x.Companies) 
.Build(); 
var commandProcessor = new CommandProcessor(command); 
var row = commandProcessor.FindOne(x.Companies);
public interface ICommandProcessor 
{ 
T FindOne<T>(); 
IEnumerable<T> FindAll<T>(); 
} 
public abstract class CommandProcessor : ICommandProcessor 
{ 
protected readonly Command _command; 
protected CommandProcessor(Command command) 
{ 
_command = command; 
} 
... 
protected abstract 
IEnumerable<IDictionary<string, object>> Execute(); 
}
IDictionary string object 
object 
IDictionary string object 
IEnumerable IDictionary string object 
object 
IEnumerable IDictionary string object
dynamic x = new DynamicCommandExpression(); 
var command = SelectAllCommand(); 
var commandProcessor = new FakeCommandProcessor(command); 
// Single row 
var result = commandProcessor.FindOne(); 
Assert.AreEqual("DynamicSoft", result["CompanyName"]); 
// Collection 
var result = commandProcessor.FindAll(); 
Assert.AreEqual(2, result.Count()); 
Assert.AreEqual("DynamicSoft", result.First()["CompanyName"]); 
Assert.AreEqual("StaticSoft", result.Last()["CompanyName"]);
public interface ICommandProcessor 
{ 
T FindOne<T>() where T : class; 
ResultRow FindOne(); 
ResultRow FindOne(CommandExpression expression); 
IEnumerable<T> FindAll<T>() where T : class; 
IEnumerable<ResultRow> FindAll(); 
ResultCollection FindAll(CommandExpression expression); 
}
public class DynamicResultRow : 
ResultRow, IDynamicMetaObjectProvider 
{ 
internal DynamicResultRow(IDictionary<string, object> data) 
: base(data) 
{ 
} 
... 
} 
public class DynamicResultCollection : 
ResultCollection, IDynamicMetaObjectProvider 
{ 
internal DynamicResultCollection(IEnumerable<ResultRow> data) 
: base(data) 
{ 
} 
... 
}
dynamic x = new DynamicCommandExpression(); 
var command = SelectAllCommand(); 
var commandProcessor = new FakeCommandProcessor(command); 
// Dynamic result 
// Requires BindGetMember overload 
var result = commandProcessor.FindOne(); 
Assert.AreEqual("DynamicSoft", result.CompanyName); 
// Typed result 
// Requires BindConvert overload 
Companies result = commandProcessor.FindOne(); 
Assert.AreEqual("DynamicSoft", result.CompanyName);
processor.FindAll<T>(x => expression<T>) 
processor.Execute() 
IEnumerable<IDictionary<string, object>> 
ToObject<T>() 
Call to a generic 
method overload 
Executes SQL 
command 
Returns native 
result 
Converts results 
to typed objects
processor.FindAll(dynamic) 
processor.Execute() 
ToObject<DynamicResultCollection>() 
IEnumerable<T> results 
Call to a non-generic 
method overload 
Follows typed 
execution path 
Converts results 
to typed objects 
Converts results 
to typed objects
MAKE 
HYBRID 
API 
NOT 
HYBRID 
WAR
LINQ expressions 
custom expression 
LINQ expression 
custom expression 
custom expression 
custom expression 
IDynamicMetaObjectProvider
Typed? Dynamic? Both! Cross-platform DSLs in C#
Typed? Dynamic? Both! Cross-platform DSLs in C#
Typed? Dynamic? Both! Cross-platform DSLs in C#

Weitere ähnliche Inhalte

Was ist angesagt?

C# Starter L04-Collections
C# Starter L04-CollectionsC# Starter L04-Collections
C# Starter L04-Collections
Mohammad Shaker
 
Contagion的Ruby/Rails投影片
Contagion的Ruby/Rails投影片Contagion的Ruby/Rails投影片
Contagion的Ruby/Rails投影片
cfc
 

Was ist angesagt? (20)

The secret unit testing tools no one has ever told you about
The secret unit testing tools no one has ever told you aboutThe secret unit testing tools no one has ever told you about
The secret unit testing tools no one has ever told you about
 
Anonymous functions in JavaScript
Anonymous functions in JavaScriptAnonymous functions in JavaScript
Anonymous functions in JavaScript
 
Scala in practice
Scala in practiceScala in practice
Scala in practice
 
Nativescript angular
Nativescript angularNativescript angular
Nativescript angular
 
Java Script Best Practices
Java Script Best PracticesJava Script Best Practices
Java Script Best Practices
 
ERRest
ERRestERRest
ERRest
 
Geek Sync | Rewriting Bad SQL Code 101
Geek Sync | Rewriting Bad SQL Code 101Geek Sync | Rewriting Bad SQL Code 101
Geek Sync | Rewriting Bad SQL Code 101
 
Refactoring
RefactoringRefactoring
Refactoring
 
C# Starter L04-Collections
C# Starter L04-CollectionsC# Starter L04-Collections
C# Starter L04-Collections
 
Rhino Mocks
Rhino MocksRhino Mocks
Rhino Mocks
 
Intro to JavaScript
Intro to JavaScriptIntro to JavaScript
Intro to JavaScript
 
#ITsubbotnik Spring 2017: Roman Iovlev "Java edge in test automation"
#ITsubbotnik Spring 2017: Roman Iovlev "Java edge in test automation"#ITsubbotnik Spring 2017: Roman Iovlev "Java edge in test automation"
#ITsubbotnik Spring 2017: Roman Iovlev "Java edge in test automation"
 
Hidden rocks in Oracle ADF
Hidden rocks in Oracle ADFHidden rocks in Oracle ADF
Hidden rocks in Oracle ADF
 
JPA 2.0
JPA 2.0JPA 2.0
JPA 2.0
 
ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in Scala
 
Refactoring
RefactoringRefactoring
Refactoring
 
Mpg Dec07 Gian Lorenzetto
Mpg Dec07 Gian Lorenzetto Mpg Dec07 Gian Lorenzetto
Mpg Dec07 Gian Lorenzetto
 
Spring 2.5
Spring 2.5Spring 2.5
Spring 2.5
 
Contagion的Ruby/Rails投影片
Contagion的Ruby/Rails投影片Contagion的Ruby/Rails投影片
Contagion的Ruby/Rails投影片
 
08 Queries
08 Queries08 Queries
08 Queries
 

Ähnlich wie Typed? Dynamic? Both! Cross-platform DSLs in C#

Share pointtechies linqtosp-andsbs
Share pointtechies linqtosp-andsbsShare pointtechies linqtosp-andsbs
Share pointtechies linqtosp-andsbs
Shakir Majeed Khan
 
Unit test candidate solutions
Unit test candidate solutionsUnit test candidate solutions
Unit test candidate solutions
benewu
 

Ähnlich wie Typed? Dynamic? Both! Cross-platform DSLs in C# (20)

Linq
LinqLinq
Linq
 
apidays LIVE Australia 2020 - Building distributed systems on the shoulders o...
apidays LIVE Australia 2020 - Building distributed systems on the shoulders o...apidays LIVE Australia 2020 - Building distributed systems on the shoulders o...
apidays LIVE Australia 2020 - Building distributed systems on the shoulders o...
 
Share pointtechies linqtosp-andsbs
Share pointtechies linqtosp-andsbsShare pointtechies linqtosp-andsbs
Share pointtechies linqtosp-andsbs
 
Dlr
DlrDlr
Dlr
 
.NET Database Toolkit
.NET Database Toolkit.NET Database Toolkit
.NET Database Toolkit
 
Understanding linq
Understanding linqUnderstanding linq
Understanding linq
 
SOLID Principles
SOLID PrinciplesSOLID Principles
SOLID Principles
 
Unit test candidate solutions
Unit test candidate solutionsUnit test candidate solutions
Unit test candidate solutions
 
Sqlapi0.1
Sqlapi0.1Sqlapi0.1
Sqlapi0.1
 
L2 Web App Development Guest Lecture At University of Surrey 20/11/09
L2 Web App Development Guest Lecture At University of Surrey 20/11/09L2 Web App Development Guest Lecture At University of Surrey 20/11/09
L2 Web App Development Guest Lecture At University of Surrey 20/11/09
 
Http4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackHttp4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web Stack
 
Тарас Олексин - Sculpt! Your! Tests!
Тарас Олексин  - Sculpt! Your! Tests!Тарас Олексин  - Sculpt! Your! Tests!
Тарас Олексин - Sculpt! Your! Tests!
 
Reactive programming every day
Reactive programming every dayReactive programming every day
Reactive programming every day
 
Angular JS2 Training Session #1
Angular JS2 Training Session #1Angular JS2 Training Session #1
Angular JS2 Training Session #1
 
Spring framework part 2
Spring framework part 2Spring framework part 2
Spring framework part 2
 
NoSQL and JavaScript: a Love Story
NoSQL and JavaScript: a Love StoryNoSQL and JavaScript: a Love Story
NoSQL and JavaScript: a Love Story
 
Tdd,Ioc
Tdd,IocTdd,Ioc
Tdd,Ioc
 
Oleksandr Valetskyy - DI vs. IoC
Oleksandr Valetskyy - DI vs. IoCOleksandr Valetskyy - DI vs. IoC
Oleksandr Valetskyy - DI vs. IoC
 
Spring and Cloud Foundry; a Marriage Made in Heaven
Spring and Cloud Foundry; a Marriage Made in HeavenSpring and Cloud Foundry; a Marriage Made in Heaven
Spring and Cloud Foundry; a Marriage Made in Heaven
 
Using xUnit as a Swiss-Aarmy Testing Toolkit
Using xUnit as a Swiss-Aarmy Testing ToolkitUsing xUnit as a Swiss-Aarmy Testing Toolkit
Using xUnit as a Swiss-Aarmy Testing Toolkit
 

Mehr von Vagif Abilov

Mehr von Vagif Abilov (8)

А нам-то зачем функциональное программирование?
А нам-то зачем функциональное программирование?А нам-то зачем функциональное программирование?
А нам-то зачем функциональное программирование?
 
Cross-platform Mobile Development using Portable Class Libraries
Cross-platform Mobile Development using Portable Class LibrariesCross-platform Mobile Development using Portable Class Libraries
Cross-platform Mobile Development using Portable Class Libraries
 
Staying Close to Experts with Executable Specifications
Staying Close to Experts with Executable SpecificationsStaying Close to Experts with Executable Specifications
Staying Close to Experts with Executable Specifications
 
SOLID Programming with Portable Class Libraries
SOLID Programming with Portable Class LibrariesSOLID Programming with Portable Class Libraries
SOLID Programming with Portable Class Libraries
 
Путь к чистому и компактному коду исполняемых спецификаций
Путь к чистому и компактному коду исполняемых спецификацийПуть к чистому и компактному коду исполняемых спецификаций
Путь к чистому и компактному коду исполняемых спецификаций
 
Practical OData
Practical ODataPractical OData
Practical OData
 
F# in Action: Playing Functional Conway's Game of Life
F# in Action: Playing Functional Conway's Game of LifeF# in Action: Playing Functional Conway's Game of Life
F# in Action: Playing Functional Conway's Game of Life
 
Executable Specifications in Action
Executable Specifications in ActionExecutable Specifications in Action
Executable Specifications in Action
 

Kürzlich hochgeladen

Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
VictoriaMetrics
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
masabamasaba
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
chiefasafspells
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
masabamasaba
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
masabamasaba
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 

Kürzlich hochgeladen (20)

WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 

Typed? Dynamic? Both! Cross-platform DSLs in C#

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6. -And look what you've done to mother! She's worn out fiddling with all your proxy classes. - There's nowt wrong wi' proxy classes, lad! I've generated more proxy classes than you've had hot dinners!
  • 7.
  • 8.
  • 9. dynamic client = WeirdWildStuffFactory.GiveMeOneOfThose(); client.NowICanPretendAnySillySentenceIsAMethodCall(); client.AndICanSendAnyArguments(1, "2", new Stream[] {}); var result = client.MeaningOfLife * 42 * Guid.NewGuid(); Assert.AreEqual(42, result);
  • 10. // Without dynamic binding ((Excel.Range)excelApp.Cells[1,1]).Value2 = "Name"; var range2008 = (Excel.Range)excelApp.Cells[1,1]; // With dynamic binding excelApp.Cells[1,1].Value = "Name"; var range2010 = excelApp.Cells[1,1];
  • 11. // With Entity Framework public User FindUserByEmail(string email) { return _context .Users.Where(x => x.Email == email) .FirstOrDefault(); } // With Simple.Data public User FindUserByEmail(string email) { return Database.Open() .Users.FindAllByEmail(email) .FirstOrDefault(); }
  • 12. Background Given Name Birth date Height John 1940-10-09 1.80 Paul 1942-06-18 1.80 George 1943-02-25 1.77 Ringo 1940-07-07 1.68 [Given(@"the following users exist in the database:")] public void GivenTheFollowingUsersExist(Table table) { IEnumerable<dynamic> users = table.CreateDynamicSet(); db.Users.Insert(users); }
  • 13.
  • 14. • This talk is not about making a choice on your behalf
  • 15. • In fact, this talk is about leaving you multiple choices
  • 16. • Moreover, this talk is about giving you an opportunity to change your mind later with minimum effort
  • 17. • This talk is about creating libraries that would expose both typed and dynamic API – but not as two different APIs
  • 18. •We will show how to make a single API that can be called from either static typed or dynamic client
  • 19. • Our hybrid API will have hybrid packaging – it will disable dynamic support when installed on platforms that lack runtime method binding
  • 20.
  • 21. dynamic results = db.Companies .FindAllByCountry("Norway") .FirstOrDefault(); // EXCEPTION
  • 22. var result = db.Companies .Where(x => x.CompanyName == "DynamicSoft") .Select( x => new { c.CompanyName, c.YearEstablished });
  • 23. var result = db.Companies .Where(x => x.CompanyName == "DynamicSoft") .Select( x => new { c.CompanyName, c.YearEstablished }); var result = db.Companies .FindByCompanyName("DynamicSoft") .SelectCompanyNameAndYearEstablished();
  • 24. var result = db.Companies .Where(x => x.CompanyName == "DynamicSoft") .Select( x => new { c.CompanyName, c.YearEstablished }); dynamic x = new DynamicQueryExpression(); var result = db.Companies .Where(x.CompanyName == "DynamicSoft") .Select(x.CompanyName, x.YearEstablished);
  • 25. • My API is my castle, any naming scheme is a matter of a personal preference
  • 26. • So we will leave method naming to personal opinion and ask a different question
  • 27. • Do you want to publish one API or two APIs?
  • 28.
  • 29. interface IQueryBuilder { From(...); Where(...); OrderBy(...); OrderByDescending(...); Select(...); } • This interface defines core operation set of our internal DSL and is shared by static typed and dynamic clients • Typed and dynamic clients differ in what kind of arguments they send to the operations: static typed or dynamic respectively
  • 30. var result = db.Companies .Where(x => x.CompanyName > "D") .OrderBy(x => x.CompanyName); dynamic x = new DynamicQueryExpression(); var result = db.Companies .Where(x.CompanyName > "D") .OrderBy(x.CompanyName); • Exposing single API with two similar looking parameter syntax flavors unifies API operations across paradigms • API users learn the same API operations no matter what paradigm they choose • API users can switch paradigms at relatively low costs
  • 31.
  • 32. LINQ expressions custom expression LINQ expression custom expression custom expression custom expression IDynamicMetaObjectProvider
  • 33. Finder.dll public interface IFinder { public class DynamicQueryExpression : QueryExpression, IDynamicMetaObjectProvider { public DynamicQueryExpression() {} } Result Find<T>(Expression<Func<T>,bool>> query); Result Find(QueryExpression query); } public class QueryExpression { // no public constructor } Finder.Dynamic.dll
  • 34. Static typed client var finder = new Finder(); var result = finder.Find<Companies>().Where(x => x.CompanyName.StartsWith("D") && x.YearEstablished > 2000); dynamic x = new DynamicQueryExpression(); var finder = new Finder(); var result = finder.Find(x.Companies).Where( x.CompanyName.StartsWith("D") && x.YearEstablished > 2000); Dynamic client
  • 35.
  • 36.
  • 37. var result1 = from c in db.Companies where c.CompanyName.StartsWith("D") orderby c.CompanyName select new { c.CompanyName, c.YearEstablished }; var result2 = db.Companies .Where(x => x.CompanyName.StartsWith("D") .OrderBy(x => x.CompanyName) .Select( x => new { c.CompanyName, c.YearEstablished });
  • 38. interface ICommandBuilder { From(...) Where(...) OrderBy(...) OrderByDescending(...) Select(...) }
  • 39. interface ICommandBuilder { ICommandBuilder From(string tableName); ICommandBuilder Where(string condition); ICommandBuilder OrderBy(params string[] columns); ICommandBuilder OrderByDescending(params string[] columns); ICommandBuilder Select(params string[] columns); Command Build(); } // Usage example var command = new CommandBuilder() .From("Companies") .Where("YearEstablished>2000 AND NumberOfEmployees<100") .OrderBy("Country") .Select("CompanyName", "Country", "City") .Build();
  • 40. public class Command { private string _table; private string _where; private List<string> _selectColumns; private List<KeyValuePair<string, bool>> _orderByColumns; ... }
  • 41. private string Format() { var builder = new StringBuilder(); builder.AppendFormat("SELECT {0} FROM {1}", _selectColumns.Any() ? string.Join(",", _selectColumns) : "*", _table); if (!string.IsNullOrEmpty(_where)) builder.AppendFormat(" WHERE {0}", _where); if (_orderByColumns.Any()) { builder.AppendFormat(" ORDER BY {0}", string.Join(",", _orderByColumns.Select( x => x.Key + (x.Value ? " DESC" : string.Empty)))); } return builder.ToString(); }
  • 42. interface ICommandBuilder { ICommandBuilder<T> From<T>(); } interface ICommandBuilder<T> { ICommandBuilder<T> Where( Expression<Func<T, bool>> expression); ICommandBuilder<T> OrderBy( Expression<Func<T, object>> expression); ICommandBuilder<T> OrderByDescending( Expression<Func<T, object>> expression); ICommandBuilder<T> Select( Expression<Func<T, object>> expression); Command Build(); }
  • 43. var command = new CommandBuilder() .From<Companies>() .Where(x => x.YearEstablished > 2000 && x.NumberOfEmployees < 100) .OrderBy(x => x.Country) .Select(x => new { x.CompanyName, x.Country, x.City }) .Build();
  • 44.
  • 45. Nobody expects to parse LINQ expression trees!
  • 46.
  • 47. var command = new CommandBuilder() .From<Companies>() .Where(x => x.YearEstablished > 2000 && x.NumberOfEmployees < 100) .OrderBy(x => x.Country) .Select(x => new { x.CompanyName, x.Country, x.City }) .Build();
  • 48. Expression<Func<Companies, bool>> expression = x => x.YearEstablished > 2000 && x.NumberOfEmployees < 100; Lambda Left Right Left Right Left Right AndAlso GreaterThan Member Access Constant Member Value Member Value 2000 Constant 100 LessThan Member Access Body Year Established Number OfEmployees Int32 Int32
  • 49.
  • 50.
  • 51. builder.From<Table>() command. Where(x => expression<Func>T, bool>> CommandExpression. FromLinqExpression(expression.Body) builder.Build() Assigns the type Assigns Where expression Converts to custom expression Evaluates the expression
  • 52. • Defined ICommandBuilder and ICommandBuilder<T> interfaces • Implemented CommandBuilder • Implemented CommandExpression • LINQ expression parsing • Expression evaluation
  • 53. interface ICommandBuilder { ICommandBuilder<T> From<T>(); } interface ICommandBuilder<T> { ICommandBuilder<T> Where( Expression<Func<T, bool>> expression); ICommandBuilder<T> OrderBy( Expression<Func<T, object>> expression); ICommandBuilder<T> OrderByDescending( Expression<Func<T, object>> expression); ICommandBuilder<T> Select( Expression<Func<T, object>> expression); Command Build(); }
  • 54. var result = db.From<Companies>() .Where(x => x.CompanyName == "DynamicSoft") .Select( x => new { c.CompanyName, c.YearEstablished }); dynamic x = new DynamicQueryExpression(); var result = db.From(x.Companies) .Where(x.CompanyName == "DynamicSoft") .Select(x.CompanyName, x.YearEstablished);
  • 55. var result = db.From<Companies>() .Where(x => x.CompanyName == "DynamicSoft") .Select( x => new { c.CompanyName, c.YearEstablished }); dynamic x = new DynamicQueryExpression(); var result = db.From(x.Companies) .Where(x.CompanyName == "DynamicSoft") .Select(x.CompanyName, x.YearEstablished);
  • 56. • Core API is static typed and packaged in assembly that doesn’t reference types from System.Dynamic namespace • API exposes a DSL based on LINQ expressions custom expression LINQ expression custom expression custom expression custom expression IDynamicMetaObjectProvider • Dynamic extensions for the API are packaged in a different assembly that is deployed on platforms with DLR support
  • 57. interface ICommandBuilder { ICommandBuilder<T> From<T>(); ICommandBuilder<object> From<T>( CommandExpression expression); }
  • 58. interface ICommandBuilder<T> { ICommandBuilder<T> Where( Expression<Func<T, bool>> expression); ICommandBuilder<T> Where(CommandExpression expression); ICommandBuilder<T> OrderBy( Expression<Func<T, object>> expression); ICommandBuilder<T> OrderBy( params CommandExpression[] columns); ICommandBuilder<T> OrderByDescending( Expression<Func<T, object>> expression); ICommandBuilder<T> OrderByDescending( params CommandExpression[] columns); ICommandBuilder<T> Select( Expression<Func<T, object>> expression); ICommandBuilder<T> Select( params CommandExpression[] columns); Command Build(); }
  • 59. interface ICommandBuilder<T> { ... ICommandBuilder<T> Where( Expression<Func<T, bool>> expression); ICommandBuilder<T> Where( CommandExpression expression); ... } // Typed client builder.Where(x => x.CompanyName == "DynamicSoft"); // Dynamic client x = new DynamicCommandExpression() builder.Where(x.DynamicName == "DynamicSoft");
  • 60. interface ICommandBuilder<T> { ... ICommandBuilder<T> OrderBy( Expression<Func<T, object>> expression); ICommandBuilder<T> OrderBy( params CommandExpression[] columns); ... } // Typed client builder.OrderBy(x => x.CompanyName); // Dynamic client x = new DynamicCommandExpression() builder.OrderBy(x.CompanyName);
  • 61. interface ICommandBuilder<T> { ... ICommandBuilder<T> Select( Expression<Func<T, object>> expression); ICommandBuilder<T> Select( params CommandExpression[] columns); ... } // Typed client builder.Select(x => new { x.Country, x.CompanyName }); // Dynamic client x = new DynamicCommandExpression() builder.Select(x.Country, x.CompanyName);
  • 62. public ICommandBuilder<T> Where( Expression<Func<T, bool>> expression) { _command.Where( CommandExpression.FromLinqExpression(expression.Body)); return this; } public ICommandBuilder<T> Where( CommandExpression expression) { _command.Where(expression); return this; }
  • 63. DynamicObject CommandExpression IDynamicMetaObjectProvider CommandExpression DynamicCommandExpression DynamicMetaObject • DynamicMetaObject
  • 64.
  • 65. Where(dynamic expression) Where(CommandExpression) DynamicCommandExpression to CommandExpression builder.Where(expression).Build() Call with dynamic argument Finds suitable method overload Converts to typed expression Follow typed builder workflow
  • 67. // Typed var command = commandBuilder .From<Companies>() .Build(); var commandProcessor = new CommandProcessor(command); var result = commandProcessor.FindOne<Companies>(); // Dynamic var x = new DynamicCommandExpression(); var command = commandBuilder .From(x.Companies) .Build(); var commandProcessor = new CommandProcessor(command); var row = commandProcessor.FindOne(x.Companies);
  • 68. public interface ICommandProcessor { T FindOne<T>(); IEnumerable<T> FindAll<T>(); } public abstract class CommandProcessor : ICommandProcessor { protected readonly Command _command; protected CommandProcessor(Command command) { _command = command; } ... protected abstract IEnumerable<IDictionary<string, object>> Execute(); }
  • 69. IDictionary string object object IDictionary string object IEnumerable IDictionary string object object IEnumerable IDictionary string object
  • 70. dynamic x = new DynamicCommandExpression(); var command = SelectAllCommand(); var commandProcessor = new FakeCommandProcessor(command); // Single row var result = commandProcessor.FindOne(); Assert.AreEqual("DynamicSoft", result["CompanyName"]); // Collection var result = commandProcessor.FindAll(); Assert.AreEqual(2, result.Count()); Assert.AreEqual("DynamicSoft", result.First()["CompanyName"]); Assert.AreEqual("StaticSoft", result.Last()["CompanyName"]);
  • 71. public interface ICommandProcessor { T FindOne<T>() where T : class; ResultRow FindOne(); ResultRow FindOne(CommandExpression expression); IEnumerable<T> FindAll<T>() where T : class; IEnumerable<ResultRow> FindAll(); ResultCollection FindAll(CommandExpression expression); }
  • 72. public class DynamicResultRow : ResultRow, IDynamicMetaObjectProvider { internal DynamicResultRow(IDictionary<string, object> data) : base(data) { } ... } public class DynamicResultCollection : ResultCollection, IDynamicMetaObjectProvider { internal DynamicResultCollection(IEnumerable<ResultRow> data) : base(data) { } ... }
  • 73. dynamic x = new DynamicCommandExpression(); var command = SelectAllCommand(); var commandProcessor = new FakeCommandProcessor(command); // Dynamic result // Requires BindGetMember overload var result = commandProcessor.FindOne(); Assert.AreEqual("DynamicSoft", result.CompanyName); // Typed result // Requires BindConvert overload Companies result = commandProcessor.FindOne(); Assert.AreEqual("DynamicSoft", result.CompanyName);
  • 74.
  • 75. processor.FindAll<T>(x => expression<T>) processor.Execute() IEnumerable<IDictionary<string, object>> ToObject<T>() Call to a generic method overload Executes SQL command Returns native result Converts results to typed objects
  • 76. processor.FindAll(dynamic) processor.Execute() ToObject<DynamicResultCollection>() IEnumerable<T> results Call to a non-generic method overload Follows typed execution path Converts results to typed objects Converts results to typed objects
  • 77.
  • 78.
  • 79.
  • 80.
  • 81. MAKE HYBRID API NOT HYBRID WAR
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87. LINQ expressions custom expression LINQ expression custom expression custom expression custom expression IDynamicMetaObjectProvider