written on Dec. 22, 2010, 5:34 p.m.
and filed in
ASP.NET MVC,
Azure
What is Windows Identity Foundation?
If you haven't heard of Windows Identity Foundation (WIF)
don't feel left out. It is one of those framework pieces that has slipped out of Redmond while nobody was looking.
The premise of WIF is that it allows you to delegate authentication and authorization to some other component. A common
need for this is a bussiness-to-business application that has users from multiple organizations that access it. For example
the Microsoft has a company store where you can go and buy Microsoft swag, software, and hardware. Since Microsoft has employees
all around the world it is unrealistic to make all of them fly to Redmond, WA to make their purchases, so Microsoft has
a relationship with another company to provide an online store where employees can go and make purchases. To simplify
access to this resource Microsoft worked with this company to use
Active Directory Federation Services (ADFS)
such that employees can use their domain credentials to access the website. Not only does this make it easier for the employee
as they only need to remember their domain credentials but it also simplies the developers job as they don't have to solve
any syncrhonization issues where new employees are hired or existing employees leave.
An example of where I have used this at work is with the MSDN Forums
and Profile. If you have ever been to a Microsoft website you know
that we all use Windows Live for authentication. One of the nice things that have been added to Windows Live was the
introduction of the Microsoft Federation Gateway
that enabled using some of the WS-* standards and SAML standards to achieve a more seemless integration.
If you want to learn more I recomend looking at the
Developer Training Kit and also checking out the work that was
done by Patterns & Practices in their Claims Based Identity & Access Control Guide.
Why does Azure change things?
One of the principles I strive for when working with Azure is to ensure "One package works everywhere". Where
this starts to break down with Azure is that WIF requires you to use the web.config to set what your Realm and STS are.
While this doesn't cause any issues if all of your environments use the same servers for this, but for us we have our
development and performance environments use a Mock STS and then our Test and Production environments go against the
real thing.
<configuration>
<microsoft.identityModel>
<service>
<audienceUris>
<add value="https://myapp.cloudapp.net/" />
</audienceUris>
<federatedAuthentication>
<wsFederation
passiveRedirectEnabled="true"
issuer="http://mock-sts.cloudapp.net"
realm="https://myapp.cloudapp.net/"
requireHttps="false" />
<cookieHandler requireSsl="false" />
</federatedAuthentication>
<applicationService>
<claimTypeRequired>
<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true" />
<claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true" />
</claimTypeRequired>
</applicationService>
<issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<trustedIssuers>
<add thumbprint="F845...AB6C" name="http://mock-sts.cloudapp.net/" />
<add thumbprint="AE67...CBA0" name="https://sts.production.com/" />
</trustedIssuers>
</issuerNameRegistry>
</service>
</microsoft.identityModel>
</configuration>
Since WIF requires these settings to be in the web.config, it means that I need to find a way to modify
these settings at runtime. I was hoping that I would be able to use the same realm for all environments
and then rely on the wreply parameter in the signon request to redirect the user back to where
they came from. As it turns out the federation partner I was communicating with did not let me do this
and instead always redirected the logged in user back to the specified realm. This meant that I had
to change the AudienceUris, wsFederation[@issue], wsFederation[@realm],
wsFederation/requireHttps, and cookieHandler[@requireSsl] all at runtime. I needed
to change the requireHttps and requireSsl at runtime as my mock STS was not configured to use SSL as
I didn't want to require this for development since it adds additional complications for the developer.
One of the things I was able to configure to make work and didn't require modification was the
trustedIssues as I was able to configure both the mock STS and the production STS certificates
as trustworthy. I'm sure there is a security reason I should not have the mock STS in there in the
production environment, but for now I decided the risk was low enough for now.
Changing things at runtime
First of all, let me just clarify that this implementation is for ASP.NET MVC 3
and I have not tested it on Webforms. One quick pre-requisite for MVC, be sure you don't
have any authorization settings in the web.config (excluding the one for the FederationMetadata) as that
will mess you up for a long time trying to figure out why this implementation does not work as you would
expect.
AA (Authentication and Authorization)
This piece took me a long time to understand and figure out, so pay close attention!! When working
with MVC if you want to require authentication for an action you just put a little [Authorize]
attribute on your action or class. When working with WIF it's almost that simple, the problem arises
when it's time to redirect the user. So to help work around that here is the AuthorizeAndAuthenticateAttribute
that I put together that will check to see if the user has been identified and if not redirect them
to the STS to authenticate while also setting the correct reply url and realm.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthenticateAndAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
public string Issuer { get; private set; }
public string Roles { get; set; }
public bool RequireSsl { get; private set; }
public AuthenticateAndAuthorizeAttribute(IConfigurationProvider configurationProvider)
{
Issuer = configurationProvider.Get("STSIssuer");
RequireSsl = Convert.ToBoolean(configurationProvider.Get("STSRequireSsl"));
}
public void OnAuthorization(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
AuthenticateUser(filterContext);
}
else
{
AuthorizeUser(filterContext);
}
}
private void AuthenticateUser(AuthorizationContext context)
{
var returnUrl = GetReturnUrl(context.RequestContext);
// user is not authenticated and it's entering for the first time
var fam = FederatedAuthentication.WSFederationAuthenticationModule;
var configuration = FederatedAuthentication.ServiceConfiguration;
var realm = GetRealm(context.RequestContext);
configuration.AudienceRestriction.AllowedAudienceUris.Add(new Uri(realm));
fam.Realm = realm;
if (!String.IsNullOrEmpty(Issuer))
{
fam.Issuer = Issuer;
fam.RequireHttps = RequireSsl;
}
var signIn = new SignInRequestMessage(new Uri(fam.Issuer), realm, returnUrl.ToString());
context.Result = new RedirectResult(signIn.WriteQueryString());
}
private static String GetRealm(RequestContext context)
{
var request = context.HttpContext.Request;
var reqUrl = request.Url;
var wreply = new StringBuilder();
wreply.Append(reqUrl.Scheme); // e.g. "http"
wreply.Append("://");
wreply.Append(request.Headers["Host"] ?? reqUrl.Authority);
return wreply.ToString();
}
private static Uri GetReturnUrl(RequestContext context)
{
var request = context.HttpContext.Request;
var result = GetRealm(context);
var wreply = new StringBuilder(result);
wreply.Append(request.RawUrl);
if (!request.ApplicationPath.EndsWith("/", StringComparison.OrdinalIgnoreCase))
{
wreply.Append("/");
}
return new Uri(wreply.ToString());
}
private void AuthorizeUser(AuthorizationContext context)
{
if (String.IsNullOrEmpty(Roles)) return;
var authorizedRoles = this.Roles.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
bool hasValidRole = false;
foreach (var role in authorizedRoles)
{
if (context.HttpContext.User.IsInRole(role.Trim()))
{
hasValidRole = true;
break;
}
}
if (!hasValidRole)
{
context.Result = new HttpUnauthorizedResult();
return;
}
}
}
If you look in the constructor you will notice that an IConfigurationProvider is passed in,
this is a simple class that first looks in the ServiceConfiguration and if the setting is not there
it will then pull the value from the web.config AppSettings section. This allows us to easily run
the websites inside of IIS or hosted in Azure without worrying about where to get any configuration
settings. This constuctor then pulls a configuration setting for what STSIssuer to use
and whether or not to require encryption.
In order to dynamically change the realm so that it matches the environment, the first step is
to get the realm based on the requested URL and then assign it to the AudienceRestriction
and use it in the constructor for the SignInRequestMessage. In order to set the Issuer
you grab a reference to the FederatedAuthentication.WSFederationAuthenticationModule and
set the Issuer property to be STS that you wish to authenticate from.
From there the rest of the code is just plumbing to handle Authorizing access based upon security groups.
written on Nov. 27, 2010, 10:34 a.m.
and filed in
django,
python
Initially one of the things I put into my domain was the ability to associate posts to multiple categories.
Wiring this functionality up in the administration interface was done automatically by the model definitions,
but providing a way to navigate from the pubic side of the website was going to be a manual process. One of
the things I was looking to avoid was writing views, I wanted to be able to still use the built in
date based generic views.
Since I am currently using the following views I was able to make use of the extra_context parameter.
To get the list of categories returned to the template I simply added the Category queryset to the extra_context
argument in the urls.py file as such.
post_day_info = {
"queryset": Post.objects.all(),
"date_field" : "pub_date",
"allow_empty" : False,
"month_format" : "%m",
"template_name": "blog/post_list.html",
"extra_context": { "categories": Category.objects.all, },
}
Now in the templates there is a categories variable that I can use to iterate over and
output the list.
<div id="category-list">
<h2>Categories</h2>
<ul class="">
{% for category in categories %}
<li><a href="{{ category.get_absolute_url }}">{{ category.name }}</a></li>
{% endfor %}
</ul>
</div>
written on Nov. 23, 2010, 5:18 p.m.
and filed in
django,
python
One of the great things about blogs is the ability to read them
offline using an RSS feed. Since this is a common need
Django provides a
Syndication framework that will simplify this task.
Simple Example
Getting started with creating an RSS feed is as simple as creating a
class that inherits from django.contrib.syndication.views.Feed
and then implements the items method to return back the
list of items that make up the feed.
from django.contrib.syndication.views import Feed
from blog.models import Post
class LatestPosts(Feed):
title = "about:thoughts - Aaron Weiker's weblog"
link = "/"
description = "Aaron Weiker's repository of shared thoughts"
def items(self):
return Post.objects.order_by('-pub_date')[:15]
As you can see from this sample it is very easy to get started with
returning the items. In addition I provided a title,
description, and a link for the feed.
But what is this example.com that you speak of?
One of the nice things about Django is that you get a lot of things
for free. With that includes the
Sites
framework. The Syndication framework will use this to provide context to
the feed so that it will know how to construct URL's.
As you can see, this is a really simple model but it does simplify how
URL information can be retrieved both inside the web app and also
outside of the web app. While I'm aware you could easily get this information
from the request URL, but where that may cause a problem is in the case
where you want to canacolize on a common host name. For example I have
www.weiker.org, weiker.org,
and www.aaronweiker.com
all pointing to aaronweiker.com and
by using the Sites framework it allows me to get the host from the site instead.
I do have all of these hosts redirecting to the master site, but that is a web
server setting and the application code should not have to make that assumption,
this is especially true during development.
Adding in the content
Up until this point we have had a feed that is solely rendered based
on the __unicode__ method of the object. If you remember,
this simply returns the title of the post and does not
have the body in the RSS description field. In order to get this wired in
we need to implement a couple additional methods.
from django.contrib.syndication.views import Feed
from blog.models import Post
class LatestPosts(Feed):
title = "about:thoughts - Aaron Weiker's weblog"
link = "/"
description = "Aaron Weiker's repository of shared thoughts"
def items(self):
return Post.objects.order_by('-pub_date')[:15]
def item_title(self, item):
return item.title
def item_description(self, item):
return item.body
Here you can see the item_title and item_description
methods were implemented. Since the __unicode__ method is used for the
title and body we could simply not implement the item_title method and rely on the
default behavior.
Filling in the rest of the details
The Syndication framework also gives us the ability to define additional attributes
that we are interested in, the pubdate and the categories.
To do this we simply need to implement some additional methods. For full details as to
what else you can do please refer to the
documentation.
from django.contrib.syndication.views import Feed
from blog.models import Post
class LatestPosts(Feed):
title = "about:thoughts - Aaron Weiker's weblog"
link = "/"
description = "Aaron Weiker's repository of shared thoughts"
def items(self):
return Post.objects.order_by('-pub_date')[:15]
def item_title(self, item):
return item.title
def item_description(self, item):
return item.body
def item_pubdate(self, item):
return item.pub_date
def item_categories(self, item):
return item.categories.all()
As you can see the implementation for this additional information is just as easy
as the title and description was. The only interesting thing to note is that
the return for item_categories must return an iterable. In this case
we can simply use the foreign key relationship to category to return back all
categories for that post.
Wiring up the URL
This as simple as using the LatestPosts class as the view.
urlpatterns = patterns('',
(url(r'^$', date_based.archive_index, post_archive_info, name="blog-index")),
(url(r'^(?P<year>\d{4})/?$', date_based.archive_year, post_year_info, name="blog-year")),
(url(r'^(?P<year>\d{4})/(?P<month>\d{2})/?$', date_based.archive_month, post_month_info, name="blog-month")),
(url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/?$', date_based.archive_day, post_day_info, name="blog-day")),
(url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<slug>[\-a-zA-Z_0-9]*)/?$', 'blog.views.detail', name="blog-detail")),
(url(r'^archive/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<slug>[\-a-zA-Z_0-9]*)/?.aspx$', 'blog.views.detail', name="blog-detail-aspx")),
(url(r'^feed/?$', LatestPosts(), name="feed")),
)
written on Nov. 22, 2010, 10:49 p.m.
and filed in
django,
python
No matter what framework you are using for building a web application a common
need will always be to create links to documents. In order to create links there
needs to be some sort of routing infrastructure in place to know how to map a
HTTP request to some code to execute. In Django
this is accomplished using the
URL Dispatcher.
Here is the current routing setup that I have for my blog:
urlpatterns = patterns('',
(url(r'^$', date_based.archive_index, post_archive_info, name="blog-index")),
(url(r'^(?P<year>\d{4})/?$', date_based.archive_year, post_year_info, name="blog-year")),
(url(r'^(?P<year>\d{4})/(?P<month>\d{2})/?$', date_based.archive_month, post_month_info, name="blog-month")),
(url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/?$', date_based.archive_day, post_day_info, name="blog-day")),
(url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<slug>[\-a-zA-Z_0-9]*)/?$', 'blog.views.detail', name="blog-detail")),
(url(r'^archive/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<slug>[\-a-zA-Z_0-9]*)/?.aspx$', 'blog.views.detail', name="blog-detail-aspx")),
(url(r'^feed/?$', LatestPosts(), name="feed")),
)
You'll notice that the URL Dispatcher uses regular expressions to define how to match requests.
In concert with this the parameters that are passed into the
view. One last thing to
pay attention to is the name that is assigned to the url. This will be used later on when
we need to get back the URL.
The easiest way now to get url to your entities when creating templates is to use the
get_absolute_url method that is defined on all models. In order to do
that though we must first implement this method on our entity, in this case a
Post.
@models.permalink
def get_absolute_url(self):
detail_args = {
"year" : self.pub_date.year,
"month" : self.pub_date.month,
"day" : self.pub_date.day,
"slug" : self.slug
}
return ('blog-detail', (), detail_args)
There are a couple things that need special attention here, first is that there is
a permalink decorator on the method and secondly that the name of the
url route that was used up above is referenced again in the return statement. If we
left out the decorator the method would not be able to resolve the absolute url.
The missing links so far is what the view looks like and then the creation of the template.
In this case the view is going to be very straightforward and just grab the item from the
database that matches the criteria passed in. Interestingly enough, this was the only
view that I needed to create to get all of the functionality I needed.
from django.shortcuts import render_to_response
from blog.models import Post
from django.shortcuts import get_object_or_404
def detail(request, year, month, day, slug):
post = get_object_or_404(Post, pub_date__year = year, pub_date__month = month, pub_date__day = day, slug = slug)
return render_to_response('blog/post_detail.html', {"post":post, "detail": True})
Then from here all that needs to be put together is the template. This template extends a
base template so all that we need to worry about is the markup for how to display the post.
<h2 class="post-title"><a id="{{ post.id }}" href="{{ post.get_absolute_url }}">{{ post.title }}</a></h2>
<div class="post-info">{{ post.pub_date }}</div>
<div class="post-body">{{ post.body|safe }}</div>
{% if detail %}
<div id="disqus_thread"></div>
<script type="text/javascript">
var disqus_identifier = '{{ post.id }}';
var disqus_url = '{{ post.get_absolute_url }}';
</script>
{% else %}
<a href="{{ post.get_absolute_url }}#disqus_thread" data-disqus-identifier="{{ post.id }}">Comments</a>
{% endif %}
There is some extra code in the view that handles working with retrieving comments from
DISQUS, but that shouldn't be confused with now. What
you'll notice here is that you don't need to provide any parenthesis when calling the
post.get_absolute_url method, in fact you will get an error if you do. The
other special thing I want to point out is that the body of the post contains HTML markup
already, so it needs to be marked as safe so that it isn't automatically encoded.
written on Nov. 22, 2010, 2:07 p.m.
and filed in
django,
python
In any application one of the first things you will need to do is define your domain. The same
still holds true when working with Django. In my case I wanted to keep things very simple
starting out and then adding complexity only when necessary. To that goal, here is the initial
model that I designed:
Then the following python code shows what would go into the applications models.py file.
The documentation for how to do this can be found
here.
import datetime
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=64)
def __unicode__(self):
return self.name
class Meta:
verbose_name_plural = "categories"
class Post(models.Model):
title = models.CharField(max_length=512)
body = models.TextField()
pub_date = models.DateTimeField('date published', null=True)
categories = models.ManyToManyField(Category)
slug = models.CharField(max_length=512, unique=True)
def __unicode__(self):
return self.title
class Meta:
ordering = ["-pub_date"]
The first thing to know is that your class will need to inherit from django.db.models.Model.
This will allow your object to get persistence to the database without needed to do a lot of special things.
Then definining your fields is as simple as giving the object a name and making it be an instance of one
of the datatype fields.
name = models.CharField(max_length=64)
In this example we are stating that the name is a character field with a maximum length of 64 characters.
If there was a field you wanted to ensure is unique a constraint can easily be added to it as we did for
the slug field. For more information on this refer to the
field types documentation.
slug = models.CharField(max_length=512, unique=True)
One of the things I made heavy use of was the administration site that comes with Django, I will go into more
detail about that in a later post. But one of the things to note is the use of the Meta
inner class to define additional details about the class. The Category.Meta class has an
attribute to provide a friendly plural name that will be used when referring to many instances of the
type Category. So to define this the attribute verbose_name_plural
is used. Then in the Post class the ordering attribute is used
to define what ordering should be used when displaying lists of that type.
Then the last bit of detail I want to mention is the __unicode__ method that is defined.
When Django gets a string representation of the object it will use the __unicode__ method
to return the text to display. If this is not defined then Django will not provide any friendly text and
will instead use the name of the class. So to provide a friendly name this method is implemented for
both classes.
written on Nov. 22, 2010, 11:04 a.m.
and filed in
django,
python
A couple months ago I bought my son (Anthony) his first programming book
Hello World! - Computer Programming for Kids and Other Beginners.
I was excited about this book because not only was it geared for kids, it also used a language
(Python) that I myself had not done anything with. So in my effort to
learn how to apply Python to what I do, I decided to learn Django
(a web framework that is similar to Ruby on Rails and
ASP.NET MVC).
To get started learning Django I went through the tutorial.
While this got me started with learning how things work, it didn't do a lot for getting used to the framework
and identifying where I might have issues. This led me to do what I always do, I decided to write a simple
blog engine. I have done this a couple times before when I was learning Azure
and RavenDB. Since both of those projects used ASP.NET MVC I was able to
bootstrap them pretty quick with things that I used to. This allowed me to get the Azure implementation functional
in about a week and then the RavenDB version functional in a weekend as that was a port of the Azure version. So
when I sat down to learn Django I was surprised when I had something functional up in one day, two days for the entire thing
that is hosting this blog post.
Here is a quick rundown of the software, frameworks, and other things that I used to get this working:
I went to spend the rest of the week writing about how I implemented this site using the above resources.
Please leave comments if you want me to dig into any specifics or have any ideas on ways that I could
make this better.
written on Oct. 23, 2010, 5:15 p.m.
and filed in
psake
A frequent requirement when creating applications that are deployed anywhere is to have the ability to develop and test the application somewhere that is not production. Simple put the developer creates the software on his computer, test validates the software does what it is supposed to in there environment, and then the users of the application use it in a production environment (I know, crazy isn’t it). A common task that needs to be done in order to accommodate this scenario is to have different configurations when running in different environments. Just think of the crazy things that a developer would do if they were developing against the production database. Or worse, a tester is trying to validate that when a user deposits money in a bank account doesn’t actually withdraw money from someone else’s account. In order to accomplish this most people resort to changing some sort of configuration setting manually when deploying the software. But we all know that humans make mistakes and are lazy so let us look for a way to make sure that we can continue to be lazy.
What is being changed?
One of the key tenets for deployments that I strive for is to minimize what changes. The ideal situation is that everything is the same in every environment. If you can do that, then stop reading now as none of this is for you. This leaves me with only wanting to change environment specific configuration settings, for example the database connection string or the endpoint to use for a web service. The type of thing that I would try to avoid changing would be an authentication provider, database provider, or anything that would result in different code being executed.
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="ApplicationServices"
connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
The above example configuration is an example a type of configuration setting that would need to be changed and for simplicity it will be used as an example.
What about the obvious?
As with most things, there are several ways to accomplish a task. As you may be aware starting in Visual Studio 2010 there is there is a new way to do configuration transformations automatically as a part of the build process. For more information on that check it out here. Part of the reason I am not using that tool is that when I initially had this need for automating deployments there were several applications and development teams involved and while we were very similar in how we developed our applications, we did things differently enough that requiring this level of management at the developer level was not practical. In addition the approach I’m going to walk through here only requires that the developer make the option easily configurable and then the people managing the deployments just need to configure the environment specific settings using a tool that they already know (or at least should), PowerShell.
What’s the deal, yo!
Enough talking, let’s look at what I’m actually talking about.
Environment Configuration
Each environment will be configured via it’s own file and the bellow file would be used to configure the above configuration file.
$global:SqlServer = "production-sql-server";
$global:Database = “production-db”;
As you can see this is very simple. The value for production-sql-server would be the name of the SQL server instance and of course production-db would be the name of the production database.
Update Script
In keeping with the idea that the simpler something is the easier it is to maintain, here is the script that is modified on an ongoing basis to control what is being modified.
Config "/configuration/connectionStrings/add[@name=ApplicationServices]/@connectionString" `
"Data Source=$SqlServer;Initial Catalog=$Database;Integrated Security=True;"
Since this script requires the use of an XPath query I fully expect that a developer will add these Config statements whenever they introduce a new option that may need to be set. You can also see that you don’t need to put the entire value into the variable, but instead just the things that could vary.
Configure Script
The idea of the update script is to make it very quick and easy to maintain, in order to achieve that the code that controls how to modify the configuration file is kept in the configure script.
param(
[string] $environment = $(throw "environment required"),
[string] $config = $(throw "config is required")
)
$doc = New-Object System.Xml.XmlDocument
$doc.Load($config)
$path = Split-Path -parent $MyInvocation.MyCommand.Definition
function Config([string] $xpath, [string] $value, [bool] $onlyIf = $true) {
if ($onlyIf -eq $true) {
$nodes = $doc.SelectNodes($xpath)
foreach ($node in $nodes) {
if ($node -ne $null) {
if ($node.NodeType -eq "Element") {
$node.InnerXml = $value
}
else {
$node.Value = $value
}
}
}
}
}
function Replace([string] $xpath, [string] $value, [bool] $onlyIf = $true) {
if ($onlyIf -eq $true) {
$nodes = $doc.SelectNodes($xpath)
$newNodes = New-Object System.Xml.XmlDocument;
$newNodes.LoadXml($value);
foreach ($node in $nodes) {
if ($node -ne $null) {
$node.RemoveAll();
$importNode = $doc.ImportNode($newNodes.DocumentElement, $true);
$newNode = $node.ParentNode.ReplaceChild($importNode, $node);
}
}
}
}
function Remove([string] $xpath, [bool] $onlyIf = $true) {
if ($onlyIf -eq $true) {
$nodes = $doc.SelectNodes($xpath)
foreach($node in $nodes) {
$nav = $node.CreateNavigator();
$nav.DeleteSelf();
}
}
}
& "$path\updates.ps1"
$doc.Save($config)
As you can tell, this script is a lot more complicated than what we have been doing before. We had to put the magic somewhere after all! This script gives you three functions that you can use in the Updates.ps1 file, Config, Replace, Remove. As you saw Config will allow you to change a value (only if it exists in the config file, it does not add it if it does not exist). Replace will allow you to switch out the selected node with a completely new node. This is handy if you need to do something a little more advanced like adding in a completely new set of elements. Of course it is best to avoid this if you can, but I had to use this in order to inject proxy server information in for certain environments. Then Remove allows you to remove a section from existing. Again, use this sparingly as this is usually a smell that indicates that behavior is changing.
Integration with psake
Since we are assuming that each environment gets packaged up the same way, we will be using the same psake build script and just passing in an argument to indicate which environment is being built. This allows our build script to stay very ignorant and only know that it has to configure something, not how it should be configured. Here is what that could look like in your psake build file:
properties {
$BuildNumber = 0
$BuildDrop = ".drop\DEFAULT"
$Environment = "DEFAULT"
$configVariables = New-Object System.Collections.Queue
$path = Resolve-Path .\default.ps1 | Split-Path
}
task default -depends Package
task Package -depends SetupEnvironment, Clean, Build, CleanEnvironment
task Build {
Robo $path\..\src $BuildDrop
Configure $BuildDrop\Web.config
}
Task SetupEnvironment {
$startVars = Get-Variable -scope Global;
$envFile = ".\ENVIRONMENTS\$Environment.ps1";
if (Test-Path -Path $envFile) {
& $envFile;
}
& ".\ENVIRONMENTS\DEFAULT.ps1";
$endVars = Get-Variable -scope Global;
foreach ($var in $endVars) {
$matchVar = $startVars | where { ($_.Name -eq $var.Name)}
if ($matchVar -eq $null) {
$configVariables.Enqueue($var);
}
}
}
Task CleanEnvironment {
while ($configVariables.Count -gt 0) {
$var = $configVariables.Dequeue();
Remove-Variable $var.Name -Scope Global;
}
}
task Clean {
if (Test-Path $BuildDrop) {
remove-item -force -recurse $BuildDrop | Out-Null
}
}
function Robo ([string] $source, [string] $destination) {
& robocopy /S "$source" "$destination" /XF *.cs *.csproj /XD obj | Out-Null
}
function Robo-File ([string] $source, [string] $files, [string] $destination) {
& robocopy "$source" "$destination" "$files"
}
function Remove ([string] $path) {
if (Test-Path -Path $path){
Remove-Item -force -recurse $path | Out-Null
}
}
function Configure([string] $file){
.\configure.ps1 -environment $environment -config $file
}
This is having the $environment variable get set via an incoming argument.
Kicking off the build
Getting things started requires running a build for each environment. To facilitate this I created a modified build.ps1 file which simply identifies each file in the ENVIRONMENTS directory and executes the build.
param (
[string] $DropRoot = ".drop",
[int] $BuildNumber = "0"
)
function Build-Environment([string] $environment){
invoke-psake -buildFile .\build\default.ps1 -properties @{ `
BuildDrop = "$DropRoot\$environment"; `
Environment = "$environment"; `
BuildNumber = $BuildNumber }
}
Import-Module .\build\psake\psake.psm1
if ((Test-Path $DropRoot) -eq $false) {
New-Item -Path $DropRoot -ItemType Directory | Out-Null
}
$DropRoot = Resolve-Path $DropRoot
Get-ChildItem .\build\ENVIRONMENTS | `
Where-Object { $_.Name -ne "DEFAULT.ps1" } | `
ForEach-Object { Build-Environment $_.BaseName }
Remove-Module psake -ea 'SilentlyContinue'
if ($Error -ne '') {
write-host "ERROR: $error" -fore RED;
exit $error.Count
}
You can also download all of the scripts in the attached zip file.
written on Oct. 22, 2010, 5:16 p.m.
and filed in
ASP.NET MVC
One of the common activities that I have found myself doing lately whenever I create a new project is to identify a way of loading configuration information and customizing the behavior of the Master Page. The way I initially went about solving this problem was to create a base class and have all of my Page ViewModel’s inherit from it. This caused two different things that I had to constantly maintain, making sure all of my ViewModel’s inherited from this MasterViewModel and then remembering to set the data every time a ViewResult is returned in an Action.
public class MasterViewModel : IMasterViewModel
{
public string SiteName { get; set; }
}
I was able to take care of the second concern by simply creating a base controller that all of my controllers would inherit from and then override View(string viewName, string masterName, object model) { … } so that the settings are automatically injected into the model.
protected override ViewResult View(string viewName, string masterName, object model)
{
((MasterViewModel)model).SiteName = "Example";
return base.View(viewName, masterName, model);
}
In order to get around the smell of forcing my ViewModel’s to inherit from a base class I put the MasterViewModel into the ViewData dictionary that is returned in the ViewResult.
protected override ViewResult View(string viewName, string masterName, object model)
{
ViewResult result = base.View(viewName, masterName, model);
result.ViewData[Data.Site] = _blogConfiguration.Configuration;
return result;
}
Once I had this in place, I created a new ViewMasterPage with a property that exposed the data that was put into ViewData. This then allowed me to have a strongly typed referenced from the Master Page.
<%@ Import Namespace="Azure.Domain.Models"%>
<%@ Master Language="C#" Inherits="Azure.Web.Views.ViewMasterPage" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title><![CDATA[<%= SiteConfiguration.Name %>]]><asp:contentplaceholder id="TitleContent" runat="server" /></title>
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
<script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.1.min.js" type="text/javascript" language="javascript"></script>
<script src="http://ajax.microsoft.com/ajax/jQuery.Validate/1.6/jQuery.Validate.min.js" type="text/javascript" language="javascript"></script>
<asp:contentplaceholder id="HeaderContent" runat="server" />
</head>
<body>
<div id="header">
<h1><![CDATA[<%= SiteConfiguration.Name %>]]></h1>
</div>
<div id="main">
<asp:contentplaceholder id="MainContent" runat="server" />
</div>
<div id="footer">
©<%= DateTime.UtcNow.Year %> <%= SiteConfiguration.Owner %>
</div>
</body>
</html>
All examples are taken from the work I have been doing as a part of creating my own blog engine on Azure. You can track my progress and grab source code on my AzureBlog project site on CodePlex.
written on Dec. 2, 2009, 9:47 a.m.
and filed in
ASP.NET MVC
I have been reading Eric Evan's
Domain Driven Design along with trying out ideas on little projects that I am doing on my own. I have recently been trying to figure out how to create and model Entity objects with their associated Value objects. In this case I started with a simple
Profile entity. For the purpose of this example just imagine
Profile as an online user profile for some sort of public online community website, this could be an extension of the ASP.NET Membership Profile Provider.

In this example it is easy to say that
Affiliation,
Avatar, and
Identity are good candidates as having their own data type. There are still two questions I have about the values associated to the
Profile entity.
- Should the value objects associated to the entity be immutable?
- Should the simple properties use simple data types or a more complex data type that is customized?
Immutability
For the case of this blog post I am going to assume that all of the properties are going to be immutable. I plan on following up on this question on a later post.
Data Types
In my previous experiments with Domain Driven Design I used the approach of using a simple data type for the value type properties where appropriate. In these experiments I also tried to abide to a purely immutable property approach. In this experiment I went with the following model for creating the value objects:

In addition the value objects also implement
ToString as well as implicit overloads for their simple data type. For example here is the code for
StringValueObject.
[caption id="attachment_43" align="alignnone" width="570" caption="Source code for StringValueObject"]

[/caption]
This still isn't really how I tried my experiment, in reality I actually added another layer such that it looked similar to this inheritance structure.

Some of the initial advantages I thought I would get from this are:
- Easier to control Null values.
- Values will always resolve using dotted notation.
- Values can be implicitly casted to their simple value types.
- Values can be extended without affecting the entity interface. For example the display name can be changed to be composed of First and Last name.
- Focus is kept on mapping to the domain instead of mapping to simple data types.
- Data validation is the responsibility of the value object class.
To finish it off, this is how I currently have my
Profile entity object looking.
[caption id="attachment_44" align="alignnone" width="570" caption="Source code for Profile object."]

[/caption]
Since this is still an experiment to learn how to better model my domain, I do not yet know if this is one horrible idea or will prove to have far more benefits than I listed above. If you have any ideas please leave a comment as I am eager to learn more.