SOLID Programming with Portable Class Libraries

Preview:

Citation preview

Vagif Abilov

SOLID programming with

portable class libraries

About myself

• Mail: vagif.abilov@gmail.com

• Twitter: @ooobject

• GitHub: object

• BitBucket: object

• Blog: http://bloggingabout.net/blogs/vagif/default.aspx

• Some articles: http://www.codeproject.com

• Some open source projects:

– Simple.Data OData adapter

– Simple.OData.Client

– MongOData

– PCL Conformance Analyzer

Poll: do you care about PCL?

• Are you familiar with the concept of PCL?

• Have you used portable class libraries?

• Have you built your own portable class libraries?

• Do you maintain source code for applications that need

to be deployed on multiple platforms?

By the way, what is «portability»?

According to Wikipedia:

Portability in high-level computer programming is the

usability of the same software in different environments.

Strategies for portability

• Transferring installed program files to another computer of basically

the same architecture.

• Reinstalling a program from distribution files on another computer of

basically the same architecture.

• Building executable programs for different platforms from source

code; this is what is usually understood by "porting".

Portabilitity definition (cont’d)

Achieving portability between different processors,

according to Wikipedia:

• Non-web programs, installed upon a computer in the

normal manner, can have more control, and yet achieve

system portability by linking to the Java package.

• Software can be recompiled and linked from source

code for different operating systems and processors if

written in a programming language supporting

compilation for the platforms.

Innovative portability strategies

• Xamarin products: compiling to native apps

– Binding Objective-C libraries (iOS)

– Binding Java libraries (Android)

• Portable class libraries: managed assemblies that work

on more than one .NET Framework platform

– .NET 4.0, 4.0.3, 4.5

– Silverlight 4, 5

– Windows Phone 7, 7.5, 8

– .NET for Windows Store applications

– Xbox 360

Recompilation vs. binary reuse

Does it really matter if we package code in

reusable assemblies? Can we compile our

code for a new target platform when we

actually need it?

PCL advantages

• If the code is not bound to a specific platform, then

packaging it in a portable class library will guard it from

unintended platform dependencies by enforcing

portability constraints at early development stage

• Packaging code as PCL may require introduction of

higher level abstractions, extraction of interfaces,

inversion of dependencies and provide guidance to

follow SOLID principles

Platform support in PCL

Authoring portable class libraries

Case study

From

Simple.Data OData adapter

to

Simple.OData.Client PCL

What is Simple.Data

• A lightweight, dynamic data access component for

.NET

• Written and maintained by Mark Rendle

• Adapters for SQL Server, Oracle, Sqlite, MongoDB,

OData, Oracle, PostgreSql, Informix

• An alternative to ORM libraries, such as Entity

Framework and NHibernate

Simple.Data code example

var db = Database.Open();

var titles = db.Albums

.All()

.Select(db.Albums.Title)

.Where(db.Albums.GenreId == 1 &&

db.Albums.AlbumId > 400);

Simple.Data OData adapter

• An alternative to WCF DataServices client

• Better fits RESTful nature of OData protocol than SOAP

alike client code generation triggered with «Add Service

Reference»

Simple.Data.OData code example

var db = Database.Opener.Open(

"http://packages.nuget.org/v1/FeedService.svc/");

var package1 = db.Packages

.FindByTitle("Simple.Data.OData");

var package2 = db.Packages

.Find(db.Packages.Title == "Simple.OData.Client");

Generate HTTP GET request URLs:

Packages?$filter=Title+eq+%27Simple.Data.OData%27

Packages?$filter=Title+eq+%27Simple.OData.Client%27

Adapter

• Structural design pattern

• Converts the interface of a class into another interface

clients expect

Common API

Adapter

External service

Simple.Data OData version <= 0.5

Simple.Data API

Simple.Data OData Adapter

OData protocol

ODataPad

Making the adapter portable

• An adapter can target new platforms as long as it

provides a bridge between interfaces (and interfaces

don’t refer to types bound to specific platforms)

• OData protocol is platform agnostic

• Most of OData adapter code deals with either parsing

XML documents returned by an OData service or

formatting CLR objects as XML documents to send to

OData service

• Simple.Data API uses types defined in Simple.Data

library

• Simple.Data supports Mono (hope of portability with

other platforms)

Targeting Windows Store apps

System.Data namespace

• Microsoft.SqlServer.Server

• System.Configuration

• System.Data

• System.Data.Common

• System.Data.Odbc

• System.Data.OleDb

• System.Data.Sql

• System.Data.SqlClient

• System.Data.SqlTypes

• System.Xml

PCL Conformance Analyzer

Demo

Simple.Data.OData version >= 0.6

Simple.Data API

Simple.Data OData Adapter

Simple.OData.Client PCL

OData protocol

Simple.OData.Client

• Version 0.13

– .NET 4.0, .NET 4.0.3, 4.5

– Windows Store

– Silverlight 5

– Windows Phone 8

• Version 0.17

– Xamarin.Android

– Xamarin.iOS

But what about SOLID principles?

Example

Adding support for authentication

User request: support authentication

• First implementation: accept user credentials (user +

password), create authentication object using one of

supported schemes (Basic, Windows etc.)

• Worked like a charm, easy to use in client code

var odataFeed = new ODataFeed(

"http://www.myservice.com/api",

"Vagif",

"Password123");

• At that time Simple.Data OData adapter included non-

portable version of Simple.OData.Client

ODataFeed

public class ODataFeed

{

public string Url { get; set; }

public string User { get; set; }

public string Password { get; set; }

public string Domain { get; set; }

public bool IntegratedSecurity { get; set; }

}

Creating Web request

var request = (HttpWebRequest)WebRequest.Create(uri);

if (this.Credentials.IntegratedSecurity)

{

request.Credentials = CredentialCache.DefaultNetworkCredentials;

}

else if (!string.IsNullOrEmpty(this.Credentials.User))

{

request.Credentials = new NetworkCredential(

this.Credentials.User,

this.Credentials.Password,

this.Credentials.Domain);

}

Merging with Portable branch

Project doesn’t compile!

• System.Net.CredentialCache: .NET 4.x only

• System.Net.NetworkCredential: most of platforms

What went wrong?

• Simple.Data OData adapter took responsibility to create

user credentials based on sensitive user information

• Leaving aside security aspects, the adapter violated

single responsibility principle

• The adapter restricted supported authentication metods

to those provided by credential creation code

• The adapter is not open to extending it with new

authentication methods, so it violated open/closed

principle too

• Use of interface segregation principle would avoid this

mistake

• PCL compliance forced use of interfaces

Revised implementation

In platform-spefic client code

var odataFeed = new ODataFeed(

"http://www.myservice.com/api",

credentials);

In Simple.OData.Client PCL

var request = (HttpWebRequest)WebRequest.Create(uri);

request.Credentials = this.Credentials;

Revised implementation

• Credentials is an instance of a class that implements

System.Net.ICredentials interface

• Neither Simple.Data OData adapter (.NET 4.x) nor

Simple.OData.Client refer to a specific authentication

scheme

• All present and future authentication schemes are

supported as long as they conform ICredentials

Observations

• Some SOLID principles require coding discipline and

leave a room for interpretation, IMHO especially SRP

and OCP (John Skeet on OCP: «While I've obviously considered the

possibility that I'm the only one who finds it confusing, I've heard enough variation in

the explanations of it to suggest that I'm really not the only one»)

• PCL conformance requirement doesn’t release you

from the responsibility to make the design decision, but

it can guard you from making obvious mistakes and

sometimes even guide you in a right direction

• PCLs make you more carefully plan service

instantiation and use of non-functional utilities (logging,

instrumentation etc.)

PCLs and concrete classes

• Portable class libraries do not push the work of

implementing platform-specific services to client

applications

• PCLs can be packaged as a single portable deployment

unit

– Autofac

– Json.NET

– Simple.OData.Client

• PCLs can also be compound, consisting of core

portable and platform-specific parts

– MetroLog

– Splat

MetroLog architecture

MetroLog NuGet specification

<files>

<file src="MetroLog.dll" target="lib\portable-

net45+wp8+win8\MetroLog.dll" />

<file src="MetroLog.dll" target="lib\net45\MetroLog.dll" />

<file src="MetroLog.NetFx.dll" target="lib\net45\MetroLog.NetFx.dll" />

<file src="MetroLog.dll" target="lib\netcore45\MetroLog.dll" />

<file src="MetroLog.NetCore.dll"

target="lib\netcore45\MetroLog.NetCore.dll" />

</files>

PCLs consuming PCLs

• A PCL client can also be a portable library

• Client target platforms must be a subset of the

referenced PCL’s target platforms

• Functionality that requires platform-specific services is

usually referred using interfaces and abstract classes

• There is a trick to use concrete platform-specific

classes in client PCLs by placing in the referenced PCL

a dummy class with the same API surface and

assembly identity as the platform-specific class

PCL profiles and portable subsets

• Profile is a set of supported platforms

• Portable subset is a family of profiles that expose

certain version of .NET FX API surface area

– Profile 78: Portable Subset:

• .NET 4.5

• Windows Phone 8

• Windows Store

– Profile 95: Portable Subset (Legacy):

• .NET 4.0.3 and higher

• Silverlight 4 and higher

• Windows Phone 7 and higher

• Windows Store

PCLs for Android and iOS

Demo: Xamarin .NET Mobility Scanner

Example: Reflection API portability

Polyglot programming with PCLs

• Use right language to solve specific problems

• C# provides the best ‘one size fits all’ choice

• F# is very efficient for immutable data transformations,

financial computations, machine learning

• F# code can be packaged in a PCL and shared among

different platforms (inluding Android and iOS!)

– No official support to target Windows Phone 8 using F# PCL, but there

is a workaround

– Both PCL and F# support in Xamarin are work in progress (with

changes being made literally while I am speaking now)

• Core logic can be written in C# and F# and packaged

as PCL, and UI is added using platform-specific tools

PCLs for the future

• Profiles for v.4.0 API surface are being deprecated

• Visual Studio 2013 can open PCLs that target legacy

platforms, but it will upgrade Silverlight to target version

5 and Windows Phone to target version 8

• Xamarin PCLs targets both v.4.0 and v.4.5 API surfaces

• If a library target wide range of platforms (both 4.0 and

4.5), its NuGet package should include separate binaries

for each surface

• Consider only targeting v.4.5 API surface for new

projects unless you need to support legacy platforms

Using PCLs in UI

• Use of portable class libraries can result in significant

code reuse in cross-platform application development

• Most popular approach to cross-platform UI with PCLs

is to use MVVM pattern and package core services,

models and view models in a portable library

• Most popular MVVM frameworks that have PCLs are

MvvmLights and MvvmCross

• MvvmCross supports targeting Xamarin.iOS and

Xamarin.Android (and provides phenomenal support at

StackOverflow by @slodge)

Example: Lions Roar

• Developed by Sequence Agency

• UI is built using MvvmCross

• View models PCL (2463 LOC)

• Entities PCL (691 LOC)

• Supported plalforms

– Windows Store (1166 LOC)

– Windows Phone 8 (668)

– Android Phone/Tablet (1172)

– iPhone/iPad (2000 LOC)

Using PCL in ODataPad UI

• ODataPad views show images

• Original view model design included core portable base

view model (without image data) and platform-specific

view models (with image data)

• Small picture size makes possible storing images in

base64 format and reuse a single view model in all

platforms

• Rendering images requires platform-specific value

converters

• A PCL with a design-time view model serves design

data to all Visual Studio designers (Blend)

Conclusion

• Portable class libraries are not only for binary reuse

• Packaging code as PCLs helps making code cleaner:

– Extract interfaces

– Unify platform-specific services

– Inject service dependencies

– Use portable data structures

• Consider PCLs when choosing third party libraries

– Ready for other platforms

– Indication of a proper design

– May only have dependencies to other portable libraries

• Consider make your next library portable even if you

only target a single platform!

Resources

• Daniel Plaisted «How to Make Portable Class Libraries

Work for You»

• Scott Hanselman «Cross-Platform Portable Class

Libraries with .NET are Happening»

Open source projects at GitHub:

• AutoFac

• MetroLog

• Splat

• MvvmCross

• Simple.OData.Client

Thank you!

• Mail: vagif.abilov@gmail.com

• Twitter: @ooobject

• GitHub: object

• BitBucket: object

• Blog: http://bloggingabout.net/blogs/vagif/default.aspx

The source code for this PCL Conformance Analyzer can

be found at https://github.com/object/PclAnalyzer

Recommended