Friday, 31 October 2014
Custom validation with DataAnnotations
You can download the source: https://github.com/stevenh77/CustomValidationWithDataAnnotations
// because sometimes you have to use MVC :) | |
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel.DataAnnotations; | |
namespace CustomValidation | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var invalidPerson = new Person() | |
{ | |
FirstName = "Tom", | |
Surname = "Tom" | |
}; | |
var validPerson = new Person() | |
{ | |
FirstName = "Steve", | |
Surname = "Hollidge" | |
}; | |
Validate(validPerson); | |
Validate(invalidPerson); | |
} | |
private static void Validate(Person person) | |
{ | |
var context = new ValidationContext(person, null, null); | |
var results = new List<ValidationResult>(); | |
var isValid = Validator.TryValidateObject(person, context, results, true); | |
if (isValid) return; | |
foreach (var validationResult in results) | |
{ | |
Console.WriteLine(validationResult.ErrorMessage); | |
} | |
} | |
} | |
class Person | |
{ | |
[ContainsTheLetterTValidationAttribute] | |
public string FirstName { get; set; } | |
[DifferentValueValidation("FirstName")] | |
public string Surname { get; set; } | |
} | |
public class ContainsTheLetterTValidationAttribute : ValidationAttribute | |
{ | |
protected override ValidationResult IsValid(object value, ValidationContext validationContext) | |
{ | |
// check type | |
var stringValue = value as string; | |
if (stringValue == null) | |
{ | |
return new ValidationResult(string.Format("Unknown type or null value")); | |
} | |
// actual validation comparison occurs here | |
return stringValue.ToLowerInvariant().Contains("t") | |
? null | |
: new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); | |
} | |
} | |
public class DifferentValueValidationAttribute : ValidationAttribute | |
{ | |
private readonly string other; | |
public DifferentValueValidationAttribute(string other) | |
{ | |
this.other = other; | |
} | |
protected override ValidationResult IsValid(object value, ValidationContext validationContext) | |
{ | |
var property = validationContext.ObjectType.GetProperty(other); | |
if (property == null) | |
{ | |
return new ValidationResult( | |
string.Format("Unknown property: {0}", other) | |
); | |
} | |
var otherValue = property.GetValue(validationContext.ObjectInstance, null); | |
// actual validation comparison occurs here | |
return (value ==otherValue) | |
? new ValidationResult(FormatErrorMessage(validationContext.DisplayName)) | |
: null; | |
} | |
} | |
} |
Monday, 27 October 2014
CSS3 Show Hide Divs
Here’s an example of showing and hiding divs with a CSS3 animation.
Demo: http://stevenhollidge.com/blog-source-code/showhide/
<!DOCTYPE html> | |
<html> | |
<head lang="en"> | |
<meta charset="UTF-8"> | |
<title>CSS3 Animations: Show/Hide divs</title> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css"> | |
<style> | |
.mainContainer { | |
border-color: #ddd; | |
border-width: 1px; | |
border-radius: 4px 4px 0 0; | |
border-style: solid; | |
padding: 15px; | |
margin: 30px; | |
width: 275px; | |
height: 260px; | |
transition: 0.4s; | |
} | |
.mainContainerBig { | |
height: 450px; | |
transition: 0.4s; | |
} | |
.section { | |
margin: 10px 0 0 0; | |
transition: opacity 1s ease-in 0.2s; | |
opacity: 0; | |
height: 0; | |
overflow: hidden; | |
} | |
.visible { | |
opacity: 1; | |
height: auto; | |
overflow: auto; | |
} | |
.labelRow { | |
margin: 20px 0 0 0; | |
} | |
.textRow { | |
margin: 0 0 0 0; | |
} | |
.bigLabel { | |
font-size: 30px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="mainContainer" class="mainContainer"> | |
<div class="btn-group" data-toggle="buttons"> | |
<label id="option1label" class="btn btn-primary active"> | |
<input type="radio" name="options" id="option1" checked> Abbreviations | |
</label> | |
<label id="option2label" class="btn btn-primary"> | |
<input type="radio" name="options" id="option2"> Actual Amounts | |
</label> | |
</div> | |
<div id='smallSection' class='section visible'> | |
<div class='row labelRow'> | |
<div class='col-md-6'>Total</div> | |
<div class='col-md-6'>Total</div> | |
</div> | |
<div class='row textRow'> | |
<div class='col-md-6 bigLabel'>560k</div> | |
<div class='col-md-6 bigLabel'>560k</div> | |
</div> | |
<div class='row labelRow'> | |
<div class='col-md-6'>Total</div> | |
<div class='col-md-6'>Total</div> | |
</div> | |
<div class='row textRow'> | |
<div class='col-md-6 bigLabel'>560k</div> | |
<div class='col-md-6 bigLabel'>560k</div> | |
</div> | |
</div> | |
<div id='bigSection' class="section"> | |
<div class='row labelRow'> | |
<div class='col-md-12'>Total</div> | |
</div> | |
<div class='row textRow'> | |
<div class='col-md-6 bigLabel'>560,456,232.00</div> | |
</div> | |
<div class='row labelRow'> | |
<div class='col-md-12'>Total</div> | |
</div> | |
<div class='row textRow'> | |
<div class='col-md-6 bigLabel'>560,456,232.00</div> | |
</div> | |
<div class='row labelRow'> | |
<div class='col-md-12'>Total</div> | |
</div> | |
<div class='row textRow'> | |
<div class='col-md-6 bigLabel'>560,456,232.00</div> | |
</div> | |
<div class='row labelRow'> | |
<div class='col-md-12'>Total</div> | |
</div> | |
<div class='row textRow'> | |
<div class='col-md-6 bigLabel'>560,456,232.00</div> | |
</div> | |
</div> | |
</div> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script> | |
<script> | |
$('label[id=option1label]').click(function() { | |
showHideDivs(false); | |
}); | |
$('label[id=option2label]').click(function() { | |
showHideDivs(true); | |
}); | |
function showHideDivs(showBig) { | |
$('#mainContainer').toggleClass("mainContainerBig", showBig); | |
$('#smallSection').toggleClass("visible", !showBig); | |
$('#bigSection').toggleClass("visible", showBig); | |
} | |
</script> | |
</body> | |
</html> | |
Sunday, 26 October 2014
How to fix orphaned SQL users
-- tells you the orphaned users | |
EXEC sp_change_users_login 'report' | |
-- to fix if you already have the server login: | |
EXEC sp_change_users_login 'Auto_Fix', 'enter your username here' | |
-- to fix if you don't already have the server login: | |
EXEC sp_change_users_login 'Auto_Fix', 'user', 'enter your login here', 'enter your password here' |
Intro.js
Here’s how to add an intro to your site:
1) Download intro.js from https://github.com/usablica/intro.js/tags (which also contains lots of example usages)
2) Add the bootstrap-responsive.min.css and introjs.css files to the head of your page (see lines 6 and 7)
3) Add data-step and data-intro attributes to the sections of your page where you want the introduction to display (see lines 11, 12 and 13)
4) Add a button or link which starts the intro (see line 14)
5) Add the intro.js file to the bottom of your page (see line 16)
<!DOCTYPE html> | |
<head> | |
<link href="bootstrap.min.css" rel="stylesheet"> | |
<!-- Add IntroJs styles --> | |
<link href="bootstrap-responsive.min.css" rel="stylesheet"> | |
<link href="introjs.css" rel="stylesheet"> | |
<!-- end IntroJs styles --> | |
</head> | |
<body> | |
<h1 data-step="1" data-intro="Welcome to my demo, this is the title of my page">Demo for intro.js</h1> | |
<p data-step="2" data-intro="Here is some more text">This page shows how easy it is to add intro.js</p> | |
<p data-step="3" data-intro="I've also included an extra UX tip for devs">You can use cookies to detect if the user is new to your page</p> | |
<a class="btn btn-large btn-success" href="javascript:void(0);" onclick="javascript:introJs().start();">Start intro</a> | |
</body> | |
<script type="text/javascript" src="intro.js"></script> | |
</html> |
Here is an example runs the intro only the first time a user visits your page
http://stevenhollidge.com/blog-source-code/introjs/index-with-cookie.html
<!DOCTYPE html> | |
<head> | |
<link href="bootstrap.min.css" rel="stylesheet"> | |
<!-- Add IntroJs styles --> | |
<link href="bootstrap-responsive.min.css" rel="stylesheet"> | |
<link href="introjs.css" rel="stylesheet"> | |
<!-- end IntroJs styles --> | |
</head> | |
<body onload="checkCookieIntro()"> | |
<h1 data-step="1" data-intro="Welcome to my demo, this is the title of my page">Demo for intro.js</h1> | |
<p data-step="2" data-intro="Here is some more text">This page shows how easy it is to add intro.js</p> | |
<p data-step="3" data-intro="I've also included an extra UX tip for devs">You can use cookies to detect if the user is new to your page</p> | |
</body> | |
<script type="text/javascript" src="intro.js"></script> | |
<script type="text/javascript"> | |
function getCookie(cname) { | |
var name = cname + "="; | |
var ca = document.cookie.split(';'); | |
for(var i=0; i<ca.length; i++) { | |
var c = ca[i]; | |
while (c.charAt(0)==' ') c = c.substring(1); | |
if (c.indexOf(name) != -1) return c.substring(name.length,c.length); | |
} | |
return ""; | |
} | |
function setCookie(cname, cvalue, exdays) { | |
var d = new Date(); | |
d.setTime(d.getTime() + (exdays*24*60*60*1000)); | |
var expires = "expires="+d.toUTCString(); | |
document.cookie = cname + "=" + cvalue + "; " + expires; | |
} | |
function checkCookieIntro(){ | |
var cookie=getCookie("introExampleCookie"); | |
if (cookie==null || cookie=="") { | |
setCookie("introExampleCookie", "1",90); | |
introJs().start(); | |
} | |
} | |
</script> | |
</html> |
Friday, 24 October 2014
Injecting a Mock into Angular Service
// http://stackoverflow.com/questions/14773269/injecting-a-mock-into-an-angularjs-service | |
angular.module('myModule') | |
.factory('myService', function (myDependency) { | |
return { | |
useDependency: function () { | |
return myDependency.getSomething(); | |
} | |
}; | |
}); | |
describe('Service: myService', function () { | |
var myService, | |
mockDependency; | |
beforeEach(module('myModule')); | |
beforeEach(function () { | |
mockDependency = { | |
getSomething: function () { | |
return 'mockReturnValue'; | |
} | |
}; | |
module(function ($provide) { | |
$provide.value('myDependency', mockDependency); | |
}); | |
}); | |
it('should return value from mock dependency', inject(function (myService) { | |
expect(myService.useDependency()).toBe('mockReturnValue'); | |
})); | |
}); |
Extending HighCharts
Demo: http://stevenhollidge.com/blog-source-code/highcharts/
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> | |
<title>Markers for column type series - HighCharts example</title> | |
<script type='text/javascript' src='./js/jquery-1.9.1.js'></script> | |
<script type='text/javascript'>//<![CDATA[ | |
$(function () { | |
(function (H) { | |
H.wrap(H.Tooltip.prototype, 'refresh', function (proceed, points) { | |
// Run the original proceed method | |
proceed.apply(this, Array.prototype.slice.call(arguments, 1)); | |
// For each point add or update trackball | |
H.each(points, function (point) { | |
// Function variables | |
var series = point.series, | |
chart = series.chart, | |
pointX = point.plotX + series.xAxis.pos, | |
pointY = H.pick(point.plotClose, point.plotY) + series.yAxis.pos; | |
// If trackball functionality does not already exist | |
if (!point.series.options.marker) { | |
// If trackball is not defined | |
if (!series.trackball) { | |
series.trackball = chart.renderer.circle(pointX, pointY, 5).attr({ | |
fill: series.color, | |
stroke: 'white', | |
'stroke-width': 1, | |
zIndex: 5 | |
}).add(); | |
} else { | |
series.trackball.attr({ | |
x: pointX, | |
y: pointY | |
}); | |
} | |
} | |
}); | |
}); | |
H.wrap(H.Tooltip.prototype, 'hide', function (proceed) { | |
var series = this.chart.series; | |
// Run original proceed method | |
proceed.apply(this); | |
// For each series destroy trackball | |
H.each(series, function (serie) { | |
var trackball = serie.trackball; | |
if (trackball) { | |
serie.trackball = trackball.destroy(); | |
} | |
}); | |
}); | |
}(Highcharts)); | |
$.getJSON('http://www.highcharts.com/samples/data/jsonp.php?filename=aapl-ohlcv.json&callback=?', function (data) { | |
// split the data set into ohlc and volume | |
var ohlc = [], | |
volume = [], | |
dataLength = data.length; | |
for (i = 0; i < dataLength; i++) { | |
ohlc.push([ | |
data[i][0], // the date | |
data[i][1], // open | |
data[i][2], // high | |
data[i][3], // low | |
data[i][4] // close | |
]); | |
volume.push([ | |
data[i][0], // the date | |
data[i][5] // the volume | |
]); | |
} | |
// set the allowed units for data grouping | |
var groupingUnits = [ | |
[ | |
'week', // unit name | |
[1] // allowed multiples | |
], | |
[ | |
'month', [1, 2, 3, 4, 6]] | |
]; | |
// create the chart | |
$('#container').highcharts('StockChart', { | |
rangeSelector: { | |
selected: 1 | |
}, | |
title: { | |
text: 'AAPL Historical' | |
}, | |
yAxis: [{ | |
title: { | |
text: 'OHLC' | |
}, | |
height: 200, | |
lineWidth: 2 | |
}, { | |
title: { | |
text: 'Volume' | |
}, | |
top: 300, | |
height: 100, | |
offset: 0, | |
lineWidth: 2 | |
}], | |
series: [{ | |
type: 'candlestick', | |
name: 'AAPL', | |
data: ohlc, | |
dataGrouping: { | |
units: groupingUnits | |
} | |
}, { | |
type: 'column', | |
name: 'Volume', | |
data: volume, | |
yAxis: 1, | |
dataGrouping: { | |
units: groupingUnits | |
} | |
}] | |
}); | |
}); | |
}); | |
//]]> | |
</script> | |
</head> | |
<body> | |
<script src="./js/highstock.js"></script> | |
<script src="./js/exporting.js"></script> | |
<div id="container" style="height: 500px; min-width: 500px"></div> | |
</body> | |
</html> | |
Wednesday, 22 October 2014
Mail Chimp UX
Generally I like Mail Chimp as a company and their UX so I might add to this blog post over time.
Validation
Nothing ground breaking here and it could be more responsive to the user as they insert data but I quite liked the green tick on green background to indicate a valid password. Perhaps the errors could be in a similar style.
Another data entry example
Home Page
Wednesday, 15 October 2014
UX Hacks: Leading the User
Here are a few quick UX tips, tricks and hacks taken from the Hacking the User Experience Pluralsight course.
Original UI:
UI redesigned to remove noise and simplify the design:
Leading the user
Visual clues added to lead the user through the process (step 1, 2 and 3):
Another option for leading the user is an introduction overlay:
A third option could be to introduce a wizard:
Some more UX resources:
Calculating color contrast in JavaScript
To calculate which foreground color to use on a background we have the following function:
function getContrastYIQ(hexcolor){ | |
var r = parseInt(hexcolor.substr(0,2),16); | |
var g = parseInt(hexcolor.substr(2,2),16); | |
var b = parseInt(hexcolor.substr(4,2),16); | |
var yiq = ((r*299)+(g*587)+(b*114))/1000; | |
return (yiq >= 128) ? 'black' : 'white'; | |
} |
Taken from: http://24ways.org/2010/calculating-color-contrast/
You can use this website to confirm it meets the W3C standards: http://snook.ca/technical/colour_contrast/colour.html
Tuesday, 14 October 2014
HTML dropdown lists and multi select
Here are a couple of options for improving dropdown lists and multi select boxes:
Select2
http://ivaynberg.github.io/select2/
Chosen
Highlighted td blocks
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<title></title> | |
<meta name="description" content=""> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<style> | |
* { | |
-webkit-box-sizing: border-box; | |
-moz-box-sizing: border-box; | |
box-sizing: border-box; | |
font-family: "Lato","Helvetica Neue",Arial,sans-serif; | |
-webkit-font-smoothing: antialiased !important; | |
} | |
html { | |
font-size: 12px; | |
-webkit-tap-highlight-color: rgba(0,0,0,0); | |
} | |
body { | |
font-family: "Lato","Helvetica Neue",Arial,sans-serif; | |
line-height: 1.42857; | |
color: gray; | |
} | |
table { | |
border-collapse: collapse; | |
border-spacing: 0; | |
background-color: transparent; | |
width: 250px; | |
max-width: 250px; | |
margin-bottom: 20px; | |
} | |
th { | |
text-align: left; | |
} | |
.table>thead>tr>th,.table>thead>tr>td,.table>tbody>tr>th,.table>tbody>tr>td,.table>tfoot>tr>th,.table>tfoot>tr>td { | |
padding: 10px; | |
line-height: 1.42857; | |
vertical-align: top; | |
border-top: 1px solid #ddd; | |
} | |
.table>caption+thead>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>th,.table>thead:first-child>tr:first-child>td { | |
border-top: 0; | |
} | |
.table>tbody+tbody { | |
border-top: 2px solid #ddd; | |
} | |
.table-condensed>thead>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>tfoot>tr>td { | |
padding: 5px; | |
} | |
.table-bordered>thead>tr>th,.table-bordered>thead>tr>td { | |
border-bottom-width: 2px; | |
} | |
.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th { | |
background-color: #f9f9f9; | |
} | |
.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th { | |
background-color: #f5f5f5; | |
} | |
table col[class*="col-"] { | |
position: static; | |
float: none; | |
display: table-column; | |
} | |
table td[class*="col-"],table th[class*="col-"] { | |
position: static; | |
float: none; | |
display: table-cell; | |
} | |
label { | |
display: inline-block; | |
max-width: 100%; | |
margin-bottom: 5px; | |
font-weight: bold; | |
} | |
.label-info { | |
background-color: #56bdf1; | |
} | |
.label-primary { | |
background-color: #1bb7a0; | |
} | |
.label-success { | |
background-color: #94b758; | |
} | |
.label-danger { | |
background-color: #fa7b58; | |
} | |
.label-warning { | |
background-color: #f3c536; | |
} | |
.label { | |
display: inline; | |
padding: .5em .8em; | |
font-size: 75%; | |
font-weight: bold; | |
line-height: 1; | |
color: #fff; | |
text-align: center; | |
white-space: nowrap; | |
vertical-align: baseline; | |
border-radius: .25em; | |
} | |
</style> | |
</head> | |
<body> | |
<table class="table"> | |
<thead> | |
<tr> | |
<th>#</th> | |
<th>Status</th> | |
<th>Player</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr> | |
<td>1</td> | |
<td><span class="label label-info">Goalkeeper</span></td> | |
<td>Julian Speroni</td> | |
</tr> | |
<tr> | |
<td>2</td> | |
<td><span class="label label-primary">Right back</span></td> | |
<td>Joel Ward</td> | |
</tr> | |
<tr> | |
<td>3</td> | |
<td><span class="label label-success">Defender</span></td> | |
<td>Scott Dann</td> | |
</tr> | |
<tr> | |
<td>4</td> | |
<td><span class="label label-warning">Midfielder</span></td> | |
<td>Mile Jedinak</td> | |
</tr> | |
<tr> | |
<td>5</td> | |
<td><span class="label label-danger">Winger</span></td> | |
<td>Yannick Bolaise</td> | |
</tr> | |
</tbody> | |
</table> | |
</body> | |
</html> |
Monday, 13 October 2014
Example of DataRepository pattern
Here is a quick example of a data repository pattern used with EF. Note the UpdateEntity method in AccountRepository purely returns the entity to be updated, this perhaps should be named more clearly.
public class AccountRepository : DataRepositoryBase<Account>, IAccountRepository | |
{ | |
protected override Account AddEntity(CarRentalContext entityContext, Account entity) | |
{ | |
return entityContext.AccountSet.Add(entity); | |
} | |
protected override Account UpdateEntity(CarRentalContext entityContext, Account entity) | |
{ | |
return (from e in entityContext.AccountSet | |
where e.AccountId == entity.AccountId | |
select e).FirstOrDefault(); | |
} | |
protected override IEnumerable<Account> GetEntities(CarRentalContext entityContext) | |
{ | |
return from e in entityContext.AccountSet | |
select e; | |
} | |
protected override Account GetEntity(CarRentalContext entityContext, int id) | |
{ | |
var query = (from e in entityContext.AccountSet | |
where e.AccountId == id | |
select e); | |
var results = query.FirstOrDefault(); | |
return results; | |
} | |
public Account GetByLogin(string login) | |
{ | |
using (CarRentalContext entityContext = new CarRentalContext()) | |
{ | |
return (from a in entityContext.AccountSet | |
where a.LoginEmail == login | |
select a).FirstOrDefault(); | |
} | |
} | |
} |
public abstract class DataRepositoryBase<T> : DataRepositoryBase<T, CarRentalContext> | |
where T : class, IIdentifiableEntity, new() | |
{ | |
} |
public abstract class DataRepositoryBase<T, U> : IDataRepository<T> | |
where T : class, IIdentifiableEntity, new() | |
where U : DbContext, new() | |
{ | |
protected abstract T AddEntity(U entityContext, T entity); | |
protected abstract T UpdateEntity(U entityContext, T entity); | |
protected abstract IEnumerable<T> GetEntities(U entityContext); | |
protected abstract T GetEntity(U entityContext, int id); | |
public T Add(T entity) | |
{ | |
using (U entityContext = new U()) | |
{ | |
T addedEntity = AddEntity(entityContext, entity); | |
entityContext.SaveChanges(); | |
return addedEntity; | |
} | |
} | |
public void Remove(T entity) | |
{ | |
using (U entityContext = new U()) | |
{ | |
entityContext.Entry<T>(entity).State = EntityState.Deleted; | |
entityContext.SaveChanges(); | |
} | |
} | |
public void Remove(int id) | |
{ | |
using (U entityContext = new U()) | |
{ | |
T entity = GetEntity(entityContext, id); | |
entityContext.Entry<T>(entity).State = EntityState.Deleted; | |
entityContext.SaveChanges(); | |
} | |
} | |
public T Update(T entity) | |
{ | |
using (U entityContext = new U()) | |
{ | |
T existingEntity = UpdateEntity(entityContext, entity); | |
SimpleMapper.PropertyMap(entity, existingEntity); | |
entityContext.SaveChanges(); | |
return existingEntity; | |
} | |
} | |
public IEnumerable<T> Get() | |
{ | |
using (U entityContext = new U()) | |
return (GetEntities(entityContext)).ToArray().ToList(); | |
} | |
public T Get(int id) | |
{ | |
using (U entityContext = new U()) | |
return GetEntity(entityContext, id); | |
} | |
} |
public interface IDataRepository | |
{ | |
} | |
public interface IDataRepository<T> : IDataRepository | |
where T : class, IIdentifiableEntity, new() | |
{ | |
T Add(T entity); | |
void Remove(T entity); | |
void Remove(int id); | |
T Update(T entity); | |
IEnumerable<T> Get(); | |
T Get(int id); | |
} |
public interface IIdentifiableEntity | |
{ | |
int EntityId { get; set; } | |
} |
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel.Composition; | |
using CarRental.Business.Bootstrapper; | |
using CarRental.Business.Entities; | |
using CarRental.Data.Contracts; | |
using Core.Common.Contracts; | |
using Core.Common.Core; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using Moq; | |
namespace CarRental.Data.Tests | |
{ | |
[TestClass] | |
public class DataLayerTests | |
{ | |
[TestInitialize] | |
public void Initialize() | |
{ | |
ObjectBase.Container = MEFLoader.Init(); | |
} | |
[TestMethod] | |
public void test_repository_usage() | |
{ | |
RepositoryTestClass repositoryTest = new RepositoryTestClass(); | |
IEnumerable<Car> cars = repositoryTest.GetCars(); | |
Assert.IsTrue(cars != null); | |
} | |
[TestMethod] | |
public void test_repository_factory_usage() | |
{ | |
RepositoryFactoryTestClass factoryTest = new RepositoryFactoryTestClass(); | |
IEnumerable<Car> cars = factoryTest.GetCars(); | |
Assert.IsTrue(cars != null); | |
} | |
[TestMethod] | |
public void test_repository_mocking() | |
{ | |
List<Car> cars = new List<Car>() | |
{ | |
new Car() { CarId = 1, Description = "Mustang" }, | |
new Car() { CarId = 2, Description = "Corvette" } | |
}; | |
Mock<ICarRepository> mockCarRepository = new Mock<ICarRepository>(); | |
mockCarRepository.Setup(obj => obj.Get()).Returns(cars); | |
RepositoryTestClass repositoryTest = new RepositoryTestClass(mockCarRepository.Object); | |
IEnumerable<Car> ret = repositoryTest.GetCars(); | |
Assert.IsTrue(ret == cars); | |
} | |
[TestMethod] | |
public void test_factory_mocking1() | |
{ | |
List<Car> cars = new List<Car>() | |
{ | |
new Car() { CarId = 1, Description = "Mustang" }, | |
new Car() { CarId = 2, Description = "Corvette" } | |
}; | |
Mock<IDataRepositoryFactory> mockDataRepository = new Mock<IDataRepositoryFactory>(); | |
mockDataRepository.Setup(obj => obj.GetDataRepository<ICarRepository>().Get()).Returns(cars); | |
RepositoryFactoryTestClass factoryTest = new RepositoryFactoryTestClass(mockDataRepository.Object); | |
IEnumerable<Car> ret = factoryTest.GetCars(); | |
Assert.IsTrue(ret == cars); | |
} | |
[TestMethod] | |
public void test_factory_mocking2() | |
{ | |
List<Car> cars = new List<Car>() | |
{ | |
new Car() { CarId = 1, Description = "Mustang" }, | |
new Car() { CarId = 2, Description = "Corvette" } | |
}; | |
Mock<ICarRepository> mockCarRepository = new Mock<ICarRepository>(); | |
mockCarRepository.Setup(obj => obj.Get()).Returns(cars); | |
Mock<IDataRepositoryFactory> mockDataRepository = new Mock<IDataRepositoryFactory>(); | |
mockDataRepository.Setup(obj => obj.GetDataRepository<ICarRepository>()).Returns(mockCarRepository.Object); | |
RepositoryFactoryTestClass factoryTest = new RepositoryFactoryTestClass(mockDataRepository.Object); | |
IEnumerable<Car> ret = factoryTest.GetCars(); | |
Assert.IsTrue(ret == cars); | |
} | |
} | |
public class RepositoryTestClass | |
{ | |
public RepositoryTestClass() | |
{ | |
ObjectBase.Container.SatisfyImportsOnce(this); | |
} | |
public RepositoryTestClass(ICarRepository carRepository) | |
{ | |
_CarRepository = carRepository; | |
} | |
[Import] | |
ICarRepository _CarRepository; | |
public IEnumerable<Car> GetCars() | |
{ | |
IEnumerable<Car> cars = _CarRepository.Get(); | |
return cars; | |
} | |
} | |
public class RepositoryFactoryTestClass | |
{ | |
public RepositoryFactoryTestClass() | |
{ | |
ObjectBase.Container.SatisfyImportsOnce(this); | |
} | |
public RepositoryFactoryTestClass(IDataRepositoryFactory dataRepositoryFactory) | |
{ | |
_DataRepositoryFactory = dataRepositoryFactory; | |
} | |
[Import] | |
IDataRepositoryFactory _DataRepositoryFactory; | |
public IEnumerable<Car> GetCars() | |
{ | |
ICarRepository carRepository = _DataRepositoryFactory.GetDataRepository<ICarRepository>(); | |
IEnumerable<Car> cars = carRepository.Get(); | |
return cars; | |
} | |
} | |
} |
This example was taken from Miguel Castro and uses MEF as it's DI container.
Friday, 10 October 2014
Resolving within Angular routing
You can have your routing code make services or data available to your controller by using resolve.
This is how we expose these functions to the controller: