|
|
-
I have been working on a Silverlight project, and the experience of using it for a period of several weeks on a real application has been quite good. I thought I would highlight three aspects that made it good.
1: It has real C# and .Net inside.
The CLR used in Silverlight may be cut-down, but the developer experience isn't. We used Visual Studio 2008, ReSharper, and all the good parts of C# 3.5: generics, extension methods, Lambda expressions and LINQ.
There is no LINQ to SQL or EF or database drivers -all data from servers comes over web services, but there is LINQ to objects and to XML. There are even third party add-ins. We used the NInject IOC container. Other IOC containers like Unity also support Silverlight now. There are also Extensions and testing tools, and mocking frameworks are getting there.
The code-run-fix cycle in Silverlight is very quick, giving immediate feedback on what your code changes have actually accomplished. I think we produced that application faster than a similar website could have been done.
2: XAML
XAML is the UI mark-up language used in Silverlight. It's also used in WPF, which is Silverlight's older brother and the successor to Windows Forms.
It's very good: all .Net coders should know the basics to XAML, unless you want to only ever work in ASP.Net. I'll rave about XAML at length given the chance, despite only being a novice. Despite its power and depth, the basics are easy to grasp. Simple Windows Forms style interfaces are easy in XAML, and it will give you room to do much more if you need to. The look and feel of the application can be set or changed in styles, and the data binding support is so good that it generally becomes a key part of a Silverlight or WPF application.
XAML encourages a separation between functionality and visual style, so that parts of the application can be handed back and forth between designer and developer as needed.
Compared to a web app where cross-browser issues are common, a Silverlight application looks the same between browsers, and even on an Apple Mac. The cross-browser issues that we had were few, and generally around integrating with the browser, or the machine's installed fonts. Compared to a desktop application, the installation is basically non-existent and the security implications lesser.
3: Developers
There is a perception that Microsoft's strategy in "taking on Flash" is to leverage the popularity of C# and get an army of low-level C# developers doing Silverlight. That perception is false in at least one respect - the Silverlight developers that I worked with were not low-level. Take a look at the ViewModel pattern that served us well.
I'm sure that you can do "put the data access code in the button-click handler" development in Silverlight if you wanted to, but the environment is rich enough to allow a lot of other ways of working that are more appropriate for larger applications.
.. and also Silverlight 3, which is now in beta, is building on these strengths.
|
-
Blogging was technically possible in the early 1990s, but only gained ground when people realised how to do it in the last 1990s and settled on the basic features - entries are shown in reverse chronological order, with comments on entries, Syndication using RSS or ATOM. It's a medium with low barriers to entry - there are a large number of blog engines, open source and not, hosted or for a variety of platforms; and largely they work in similar ways. To the point where is has been said that "Blogging apps are the new Hello World".
I'm a fan of the social web, which adds a lot more than basic blogging. But the social web that most people know operates in walled gardens, on FaceBook, Orkut, LiveJournal, Twitter or the like offering extra features at the cost of locking you into only using them when interacting with other users of the same site. I don't think that it necessarily has to be that way. Blog engines just have to interoperate a bit more richly to enter the same space and open it out into a game where anyone can play.
OpenId I had hoped would be the key to authenticating users whose home is elsewhere. OpenId has not yet taken the world by storm, but the basic idea is sound, so I hope that they keep refining and promoting their idea so that a later version or derivative catches on, and that it's not called "facebook connect" - Punting the whole thing into one of the walled gardens is not a great idea.
Here are some ideas to push blogs in a direction that favours a completely distributed approach to social webworking:
1) Be an OpenId consumer to support the "casual but identified commenter" scenario.
2) Be anOpenId provider so that the blog engine can be a source of identity.
3) Support machine-readable data about the blog user, including friends' lists. A standard for this is FOAF.
4) Support feeding content to friends only. This last step has no fixed standard yet, but it the key value-added service that the social networking walled gardens provide.
|
-
A few days ago, I vented my frustration with some ... complex and unintuitive software behaviour by saying on twitter: "TFS, why do you fail to *undo* a checkout due to your bogus unspecified supposed locked files? I can has SVNburger?" I did work around the problem within a few hours, but not before I got this unexpected reply:
JasonBarile said Can you send me more details on your TFS undo question? I guess that he found this is due to monitoring usage of the phrase "TFS" on twitter. JasonBarile is, according to his blog at http://blogs.msdn.com/jasonba/ "a Principal Test Manager for Visual Studio Team Foundation Server at Microsoft"
So I did send him details, and a few tweets and emails later he had replicated the issue and was offering to send me a hotfix when it was available. (I don't think I need it, we've fixed the issue at hand.)
Now if, like many people, you've been wondering if twitter is more than just a fun toy, here is an answer. This is an example of using twitter well; how, by caring about the product that you're making and by keeping an eye on your users' chatter, you can produce an unexpectedly good user experience.
The other lesson is that you're not just talking to your subscribers. The latest generation of communications tools builds in searchability and watchability.
|
-
Over the holidays, for some light holiday reading while resting the bruises from ski-slope related activities, I took Bill Wagner's book "More effective C#" it showed a few ways in which C#3's anonymous types can be passed to methods, which i had thought was beyond thier scope.
Given this definition:
var anonTypedObject = new { Name = "Fred", Balance = 12 };
All we can say about it is that it's descended from Object, so it can be passed to a method ike this:
static void ShowTypeOfObject(Object obj) { Console.WriteLine("(O) Type is " + obj.GetType().ToString() + " value is " + obj.ToString()); }
ShowTypeOfObject(anonTypedObject) produces output of:
(O) Type is <>f__AnonymousType0`2[System.String,System.Int32] value is { Name = Fred, Balance = 12 }
This also shows that that the generated anonymous type is itself a kind of generic tuple.
However strongly-typed methods can also accept the anonymous type, if the method is generic. This version works much the same:
static void ShowTypeGeneric<T>(T t) { Console.WriteLine("(G) Type is " + t.GetType().ToString() + " value is " + t.ToString()); }
ShowTypeGeneric(anonTypedObject);
But what useful work can a generic method do on an anonymous type? The generic method has to make sense for any type from object on down, so it knows little useful about the type. But it can also be passed a function to apply to the data, which does know what to do with the anonymous type. If it's an anonymous function, of course. In a more complex example the generic method might check nulls or iterate a list as well as applying the worker method, but here's a very simple example:
static void ShowFuncOutput<T>(Func<T, string> func, T data) { Console.WriteLine(func(data)); }
ShowFuncOutput(v => "Lambda: Value is " + v.ToString(), anonTypedObject);
Which produces output as you might expect;
Lambda: Value is { Name = Fred, Balance = 12 }
Though if it gets much more complex than this, I would recommend paying the cost of a non-anonymous type, it's likely to be worth it in the long run.
|
-
(Part 1 is here)
The main part of the Openid support is in the controllers and in the view. Fortunately the heavy lifting has been done already, with a library called DotNetOpenId and sample code that started life in Rob Connery's storefront sample, and was worked on by my colleagues Howard Van Rooijen and Pete Worthington before I got hold of it.
First, install the DotNetOpenId library and reference it from the Oxite.MVC project
Add an action called OpenIdSignIn to AccountController:
public ActionResult OpenIdSignIn()
{
var openid = new OpenIdRelyingParty();
if (openid.Response == null)
{
string claimedUrl = Request["openid.claimed_id"];
// Stage 2: user submitting Identifier
Identifier id;
if (Identifier.TryParse(claimedUrl, out id))
{
IAuthenticationRequest request = openid.CreateRequest(claimedUrl);
// email and display name would be nice, but we can do without it
ClaimsRequest claimsRequest = new ClaimsRequest
{
Email = DemandLevel.Request,
Nickname = DemandLevel.Request
};
request.AddExtension(claimsRequest);
request.RedirectToProvider();
}
else
{
ModelState.AddModelError("_FORM", "Invalid identifier");
return View("SignIn");
}
}
else
{
// Stage 3: OpenID Provider sending assertion response
switch (openid.Response.Status)
{
case AuthenticationStatus.Authenticated:
{
ClaimsResponse claimsResponse = openid.Response.GetExtension<ClaimsResponse>();
string nickname;
string email;
// did we get a claims response?
if (claimsResponse == null)
{
nickname = openid.Response.ClaimedIdentifier.ToString();
email = string.Empty;
}
else
{
nickname = claimsResponse.Nickname;
email = claimsResponse.Email;
}
LogOpenIdLogin(openid.Response.ClaimedIdentifier, nickname, email);
return RedirectToRoute("Home");
}
case AuthenticationStatus.Canceled:
ModelState.AddModelError("_FORM", "Canceled at provider");
return View("SignIn");
case AuthenticationStatus.Failed:
ModelState.AddModelError("_FORM", openid.Response.Exception.Message);
return View("SignIn");
}
}
return new EmptyResult();
}
private void LogOpenIdLogin(Identifier claimedIdentifier, string nickname, string email)
{
// if the user record does not exist, create it
string userName = claimedIdentifier.ToString();
IUser user = MembershipRepository.GetUser(userName);
if (user == null)
{
Guid defaultLanguageID = LanguageRepository.GetLanguage(Config.Site.LanguageDefault).ID;
MembershipRepository.AddOpenIdUser(userName, nickname, email, defaultLanguageID);
}
FormsAuth.SetAuthCookie(userName, false);
}
And in the SignIn.aspx view add a form for the OpenId to be entered:
<div id="openid">
<h2>Use Your Open ID</h2>
<div id="spacer"></div>
<form method="post" action="<% =Url.Action("OpenIdSignIn") %>" class="login">
<!-- BEGIN ID SELECTOR -->
<input type="text" class="openidinput" size="40" id="openid.claimed_id" name="openid.claimed_id" />
<script type="text/javascript" >
idselector_input_id = "openid.claimed_id";
</script>
<script type="text/javascript" id="__openidselector" src="https://www.idselector.com/selector/9ef50c2962fc7066564dc829214efbe0ef945335" charset="utf-8"></script>
<!-- END ID SELECTOR -->
<input type="submit" value="<%= Localize("Login") %>" class="submit button" />
<br />
</form>
<div class="hint">
Open ID is an identity system that allows you to sign in to websites with a single account.
Your username and password are secure with Open ID - and you don't have to remember yet another password.
</div>
</div>
And that's all.
We are using a nice bit of Javascript Ui from IdSelector.com which, as you can see, has a serial number in it. Unfortunately they've stopped issuing these. I hope a good replacement comes along.
There’s lots more that can be done with OpenId here. For instance, the OpenId login form would be useful on the comments page - OpenId users are more likely to be just casual commenters, whereas the people writing the blog could take the time to make a local login. The users created via OpenId do not have any permissions, so they can add comments but not post; which is a sensible default.
I have more than one OpenId, so it would be nice to group them together into my account. Oxite is not yet an OpenId provider, which would be interesting.
A mature system would be able to block open ids based on patterns in the url, allowing or disallowing providers based on security considerations. But Oxite first should gain ability to list users, and change their permissions – e.g. admin, editor, contributor or locked out, and track the user's time and IP of the last login, etc
I’m sure that I made mistakes in this implementation – there’s lots that I don’t know about MVC and about OpenId.
|
-
I am following up on my last post on Oxite by showing how to add OpenId login support to it. I won't finish this today. This first one will cover the repository changes needed.
First, download the Oxite source and set up the database. From a database perspective, the table where user logins are stored is called oxite_user. In code this is referenced via the IUser interface.
In general, I would like to use the OpenId "claimed identifier" as the user name. User names have to be unique, so this the user name “http://AnthonySteele.myopenid.com/” is used up by my OpenID login, but I think we can live with that. We can tell OpenId logins from regular ones by eye (OpenId logins look like Urls), but I'd like a flag to do it unambiguously.
In the code this flag is an enumeration:
namespace Oxite.Data
{
/// <summary>
/// How does the user log in
/// </summary>
public enum UserLoginMethod: byte
{
/// <summary>
/// Default/unknown value
/// </summary>
Unknown = 0,
/// <summary>
/// The user has a local user name and password
/// </summary>
LocalLogin = 1,
/// <summary>
/// The user logs in using an openId from a different site
/// </summary>
OpenId = 2
}
}
There could be Cardspace or some other kind of login later. the flag needs to be stored on the user data in the database, with all existing logins marked as “local login”. In SQL, execute:
alter table oxite_user
add UserLoginMethod tinyint not null default 1;
Add the login method to the IUser interface:
public interface IUser
{
... as before...
UserLoginMethod LoginMethod { get; set; }
Then fix the fake implementation of IUser with an auto property:
public class FakeUser : IUser
{
#region IUser Members
... as before...
public UserLoginMethod LoginMethod { get; set; }
Then do some drag and drop to regenerate the LINQ data classes, which will give the database-backed User class a property UserLoginMethod, of type short. Now we want the strongly-typed wrapper:
partial class oxite_User : IUser
{
#region IUser Members
... as before ...
public UserLoginMethod LoginMethod
{
get
{
return (UserLoginMethod)UserLoginMethod;
}
set
{
UserLoginMethod = (byte)value;
}
}
The Oxite membership repository does not yet have a way to add users, so we need to add one:
public interface IMembershipRepository
{
... as before ...
/// <summary>
/// Add a new user with an OpenId login
/// </summary>
/// <param name="userName">The claimed identifier</param>
/// <param name="displayName">the name to use onscreen</param>
/// <param name="email"></param>
/// <param name="defaultLanguageId"></param>
/// <returns></returns>
IUser AddOpenIdUser(string userName, string displayName, string email, Guid defaultLanguageId);
This is implemented in the fake repository as follows:
public IUser AddOpenIdUser(string userName, string displayName, string email, Guid defaultLanguageId)
{
FakeUser newUser = new FakeUser();
newUser.ID = Guid.NewGuid();
newUser.Username = userName;
newUser.DisplayName = displayName;
newUser.Email = email;
newUser.LoginMethod = UserLoginMethod.OpenId;
Users.Add(newUser);
return newUser;
}
And in the Linq to SQL repository as follows. This isn't perfect (particularly the choice of salt should be unique to the user, and it's not clear how the email should be hashed), but it will do until more complete user-adding code is in place in Oxite.
public IUser AddOpenIdUser(string userName, string displayName, string email, Guid defaultLanguageId)
{
oxite_User newUser = new oxite_User();
newUser.ID = Guid.NewGuid();
newUser.Username = userName;
newUser.DisplayName = displayName;
newUser.Email = email;
newUser.PasswordSalt = "NaCl";
newUser.Password = saltAndHash(userName, newUser.PasswordSalt);
newUser.HashedEmail = saltAndHash(email, newUser.PasswordSalt);
newUser.LoginMethod = UserLoginMethod.OpenId;
newUser.DefaultLanguageID = defaultLanguageId;
newUser.Status = 1;
dataContext.oxite_Users.InsertOnSubmit(newUser);
dataContext.SubmitChanges();
return newUser;
}
So that’s the repositories and domain objects. Next: the controller and view code to make OpenId logins possible.
|
-
Today I am looking at Oxite. Not this Oxite, but this Oxite. It’s a sample application on the ASP.NET MVC platform. It’s a blog engine, so it’s doubly interesting to me. New MVC blogging engines crop up on CodePlex every month or so (and I have contributed code to BlogSvc). Generally they pass without much (or any) notice.
Oxite, however, arrived with an impressive degree of completeness, and generated a lot of publicity. Not all of it was accurate – some seemed to think it was a major new part of Microsoft’s internet strategy rather than a sample app by a couple of guys at Microsoft. As it stands, it's not a competitor to WordPress. I like WordPress, I have deployed and configured it and several times. While I'd like something like that which I can also hack in C#, Oxite is not there. Yet.
Code reviews followed. See Rob Connery's blog, here and here for details. Oxite is clearly not ready for general use as finished software, it’s not even ready as a best-of breed code sample, but developers may be interested. Oxite has a few code issues (see Rob Connery’s links above), but it's also got advantages:
Advantage 1: The guys who wrote it have put a lot of time and work into it - they can afford to, it's part of their day job.
Advantage 2: Oxite got instant publicity and community, including a code review from Rob Connery, and lots of other people poking at it- orders of magnitude more downloads and comments than the other MVC blog engines on codeplex got, combined.
I'm watching Oxite with interest - I want to know if they will take the advice on board, and will it become more than just a "sample app".
Next time I will show putting OpenId support into Oxite, with help from DotNetOpenId and sample code from Rob Connery via a few of my colleagues.
|
-
A while back (has it been two years already?) I wrote about using the new generics feature in C# to make strongly typed lists, and subclassing generic lists as a way of making lists that are both strongly typed and have type-specific methods attached, and doing it with the bare minimum of coding effort.
This technique is not widely used, for better or worse. Today I'd like to look at a few ways that this would differ if done in C# 3.5 using extension methods.
In an extension method, "this" can be null.
The code someObject.operation(); will normally produce a null reference exception right away if someObject is null. But not in an extension method. This quirk is used to great effect in the null-test extension method posted on Stackoverflow:
public static class Extensions { public static void ThrowIfArgumentIsNull<T>(this T obj, string parameterName) where T : class { if (obj == null)
throw new ArgumentNullException(parameterName + " not allowed to be null"); } }
You have more choice as to which type to attach an extension method
Rather than write public static int Sum(this List<int>items), you can also type items as one of the interface types that List<int> supports - as IList<int> or IEnumerable<int>. In general, you should use the weakest type that it is sensible to attach the operation to, so as to make the operation usable in the widest circumstances. A method typed as public static int Sum(this IEnumerable<int>items) can also be used on arrays of ints, for instance. This makes it more flexible.
Extension methods are separate
Extension methods do not necessarily travel with the class that they work on. They are declared in separate class, and potentially separate namespaces and assemblies. I have mentioned this before, but it raises the design possibility that appropriate extension methods could be used to make objects gain different functionality in data and display layers parts of an application, without causing dependencies to be placed in the object itself.
The extension method model offers new, simple and very flexible ways of working with strongly-typed collections of objects.
|
-
The ASP.NET Model-View-Controller kit has finally gone into beta. Blog posts from Scott Guthrie, Scott Hanselman and Phil Haack give more details.
The five preview releases that have preceded it were well received, and the Beta finally brings this exploratory and community design feedback phase to a close, so that there should be few changes between the beta and the final release. Scott Guthrie summarised the project's progress as "not 100% feature complete, we think the major subsystems are all getting really close to being done, and that the quality level is now pretty good".
The MVC kit is an interesting addition to the ASP.NET framework, and is a good alternative to the ASP.NET Webforms.
But the big question is, is it ready? Can you develop a site against this beta?
Yes, it comes with a go-live licence.
But should you?
I think so. www.stackoverflow.com is live and facing the internet right now, and is built on it. Jeffrey Palermo is doing "several enterprise client applications" on it.
If you like TDD, simple URLs, and the web way of working rather than ASP.NET WebForms' stateful model, URL routing and other Ruby on Rails-inspired goodness, then the advantages of the MVC kit should outweigh the remaining bugs.
It's going to be an important part of ASP.NET 4.0, so it's well worth getting to know. It's the first Microsoft download to come with jQuery. and apparently, chicks dig it.
|
-
In the 1990s I coded on a few systems where the architecture was that we attached database functionality to our business objects, so you could do something like
someObject.Value = 4;
someObject.Save();
While this looks appealing, this pattern (I later learned that it is called active record) has fallen out of favour, in favour of a model where the database code is contained not in each object, but in a layer that deals with database and nothing else, e.g:
someObject.Value = 4;
dataLayer.Save(someObject);
Now the data layer depends upon the object model, and not vice versa. Database code is grouped together, and is easier to change or replace. These days it's often services as well as databases that data comes from and goes to.
We have the "layered" application as the default mainstream pattern. However there have been new ideas percolating. Inversion of control gives us tools to manage and rearange these layers.
I read Jeffrey Palermo's Onion Architecture and found myself nodding in agreement. I don't really mind how much it resembles Hexagonal, it's good either way.
This is a workable, flexible model, where the object model is made from Plain Old Objects with no special attributes, interfaces or base classes, and knows nothing about any persistence, service or UI layers.
I have also been watching Rob Conery's ASP.net MVC storefront demo video series. It's excellent. Despite supposedly being about the asp.net MVC kit, it shows a lot of good things about a LINQ to SQL data layer, IOC, mocking and testability. And the pattern is much like the onion. (These videos may no longer be on the asp.net MVC site. You can find them at http://blog.wekeroad.com/mvc-storefront/ )
But the problem with LINQ to SQL in the default configuration is that the object model is not central, it is in the LINQ data layer.
There are two ways around this,
First, make LINQ to SQL worth differently, to instead make LINQ to SQL use the object model rather than its own objects. Ian Cooper shows how to do this in his series.
Secondly, you can use the objects generated by LINQ to SQL as data transfer objects only, and copies data from them into the object model. Rob Connery takes this approach and it is surprisingly painless.
In part 10, Rob Connery does something surprising to me - he makes the Shopping cart object aware of the repository interface, so that it can save dependant data. It is given a repository interface on creation, in a Dependency Injection pattern.
This is interesting, this object is using the active record pattern? And if so, is it a bad thing? The coupling that made this a bad idea originally is not present.
To my mind this arangment is not ideal. It may be better to push this code out one layer and attach it to the domain object with extension methods. Extension methods in general give an interesting new possibility - that the object itself is just a data container, but with the right extensions in play it knows how to save itself, or display itself, or whatever the current layer requires.
|
-
Linq to SQL is great. You can open up your Db schema, drag some tables in, and in no time you have a data context class with loads of functionality, and data classes for all of the entities. No more recordsets to unpack into objects, no more fields to pack into SQL params, just lists of strongly typed ojects. Even better, the classes are partial so that you can extend them.
It seems natural to extend the data context by adding methods like var orders= MyDataContext.GetOrdersForCustomer(myCustomer.Id);
Methods like these are trivial to implement with Linq:
from o in orders where o.CustomerId = customerId select o;
And Linq makes persistence simple too.
Now the problem comes in when you think about layering and mocking. You'd like to have a interface e.g. "IDataStore", with an implementation of it that is backed by the database, and another mock implementation for testing. You'd like to have the data classes to be defined where they depend on neither of these implementations, but both implementations can reference them.
But Linq to SQL's code isn't laid out like this if you drag and drop. The DataContext doesn't implement any interfaces. The data classes are defined in the same namespace as the DataContext. You can test the data classes by just creating and populating them, but you can't test anything that depends on data retrieval without using the datacontext and behind it, the database.
It's taken me a while to get there, but this seems to be the problem that Ian Cooper is solving in Architecting LINQ To SQL Applications, part 5
Hence the"long way around" to seeing why Ian was on about that. Linq to SQL can be used in other ways besides drag and drop, and some of these ways will give you what you want.
|
-
I heard again recently that "A method should have only one return statement" and of "The single-return law", that more than one return was a poor coding practice. I disagree.
public int IsThisCodeSoBad(int param) { if (param == 0) return 0;
foreach(foo in bar) { If foo.fish() { return 1; } }
return 2; }
I have never seen any evidence for the belief that that multiple exit points pose a coding risk, or are bad style. I do not know of any formal study that has found this to be the case, on C# code or on code in any other language.
Coding horror recently talked about "Spartan programming", and mentioned "frugal use of control structures, with early return whenever possible."
One of the few links on the topic that I could find was "Return statements and the single exit fantasy":
http://www.leepoint.net/JavaBasics/methods/method-commentary/methcom-30-multiple-return.html
Take a look at some of the comments on the Daily WTF (http://thedailywtf.com/Articles/Refactoring_to_the_Max.aspx ) where this issue is raised, particularly this one:
Re: Refactoring to the Max
2006-04-05 02:25 • by John Hensley
The main reason for the single return "law" in C is to make sure you clean up memory, locks, handles, etc. before you leave a function.
This, on the other hand, is Java code.
Apparently the reason for this "law" was that when coding in C or the like, it was natural to deallocate resources near the end of the function. Thus any extra exit points were an invitation to memory leaks. There are two reasons why this is no longer the case:
Firstly, garbage collected languages make explicit deallocation unnecessary in most cases.
Secondly, try...finally blocks and using statements allow deallocation to happen with greater certainly at the end of any block of code when it is needed.
In any event, a function with multiple exit points is a far lesser issue than a goto. In some cases it is the simplest way to code now that we have control structures to deal with it. But beware of returns in the middle of loops or highly nested code. All other things being equal, avoid them, since they are less readable and predictable.
It is absolutely fine to use it as a first check on the parameters after entering a function, before getting down to the serious work and allocating those resources,– it's more readable than embedding the rest of the function in an if-block. However throwing an exception is the more usual case for this.
My view of the matter is:
- There are cases where a single return is more readable, and cases where it isn't. See if it reduces the number of lines of code, makes the logic clearer or makes the control structures less deeply nested.
- More than one return style may be a bad habit in C code, but there is no evidence for it being bad in more modern languages.
- There is no law requiring only one exit point for a method. Some people have an unsubstantiated belief in the superiority of this style.
- Requiring fewer return statements for purposes of centralising resource deallocation is not a common need in modern languages that have garbage collection and
try..finally blocks.
Therefore, use as many returns as suits your artistic sensibilities, because it is a layout and readability issue, not a technical one.
There's another issue at play here, about rules being adhered to without people grasping the reason why they are adhering to them, and thus keeping the rule in force after the need for it has evaporated. Rules should be given along with reasons for them.
Addendum:
Watching the DailyWTF commentators (who have a fairly high opinion of their own coding skills, given that they are essentially gathered there to point and laugh) try and repeatedly fail to translate the following code into a single expression underscores that the multiple-exit points version can be in some cases easier to understand, read, and modify: if (condition1(x)) return true; if (condition2(x)) return false; if (condition3(x)) return true; return false;
You'd think that this is the same as
return condition1(x) || !condition2(x) || condition3(x);,
However the one-liner version returns true if all conditions are false, unlike the original. Furthermore, it evaluates condition3 if condition2 is true, which the original does not.
You could also code it as:
bool result = false;
if (condition1(x)) result = true; if (condition2(x)) result = false; if (condition3(x)) result = true;
return result;
However this also evaluates condition3 if condition2 is true, which the original does not. I'm sure that the original logic can be replicated with only one return, but it's not going to be as elegant.
Update: Richard Tobin has sent me this comment by email:
This discussion is a common one that we continually face where I work. I took your example and reworked it into a single return as follows: private bool LotsOfReturns(int x) { if (Condition1(x)) return true; if (Condition2(x)) return false; if (Condition3(x)) return true; return false; }
private bool OneReturn(int x) { bool flag = Condition1(x);
if (!flag) { if (!Condition2(x)) { flag = Condition3(x); } }
return flag; } After compiling and looking at the IL with Reflector, the single return routine seemed more compact and efficient, and the C# source seems less tricky.
All I can really say is that he has suceeded in converting the code, and which one "seems less tricky" is going to be a matter of taste.
|
-
I've had Microsoft Source Analysis (AKA StyleCop) installed for a couple of weeks. Yesterday I have very belatedly taken the plunge into Resharper.
I have gotten used to the style promoted by Source Analysis. I've gotten into the zone of feeling that warm fuzzy glow from code having no warnings. Howard van Rooijen calls this something akin to OCD and he may be right.
As an aside, if you're coding in C#, I feel that you should get used to the coding style that this tool promotes. It's the most authoritative standard layout that there is, and will most likely be widely used. It's the style that has the backing of Microsoft, and has tools for checking adherence. Other styles don't have those advantages.
All I've done with it so far with Resharper is similar OCD-type things, clicking on what it flags up and deciding to suppress or fix the message for a similar warm-fuzzy feeling when the code is clean again.
There were reasons why I didn't install Resharper sooner - resharper 4.0 has just recently come out of beta, and now we have licences on request. So far, Resharper has been excellent and you should try it at your convenience.
Unfortunately, the two tools don't play well together on all machines, and my laptop is affected. When I edit project properties I now get an obscure error: "COM object that has been separated from its underlying RCW cannot be used."
This error is mentioned here and here.
I now have to say goodbye to one of these tools until this situation is resolved. From this laptop, Source Analysis has gone on holiday. Hopefully it will come back soon, feeling refreshed and ready to help me out. Resharper and I are waiting.
|
-
Just a quick note on something that was on my mind.
I knew someone who lived on a "Seven Sisters road", at number 20. She knew that "Seven Sisters" was a really bad name for a road. Just picture yourself phoning for a taxi at "Number 20, Seven Sisters road". Say it three times quickly if you're not hearing the issue.
Also "One" was a bad name for a train company - Imagine anouncements like "The 12:20 One service to Basingstoke is on time on platform three" People who arrive on platform 3 at 12:20:15 are going to be sorely disapointed.
Is there a moral for techies in this? Maybe. But it is a user interface design issue in the broader sense, and shows how user interfaces can mislead people, impair thier performance and cause frustration in ways that the original designer did not anticipate. Often "getting it right" just means not falling into any of a large number of potential pitfalls. Some of which are not obvious.
|
-
WFC is well known to be brilliant at SOAP, and fans of WCF may be aware that in version 3.5 may be aware of the cool new features. But the rest of us (the REST of us, haha, I'm so funny) may not know that it addresses in a simple and flexible way other scenarios- that is, serving XML or JSON data off simple Urls in a REST style.
This note is be based information from talks by Aaron Skonnard (http://www.pluralsight.com/blogs/aaron/) and Richard Blewett (http://www.dotnetconsult.co.uk/weblog2/) given at Devweek. I went to these talks to get a basic understanding of WCF, and found a couple of interesting features as well.
Before the REST in introduced , I'll start with a simple service that when you give it your name, will greet you.
The contract is:
using System.ServiceModel;
namespace GreeterLib
{
[ServiceContract]
public interface IGreeterService
{
[OperationContract]
string Greet(string name);
}
}
The service implementation is
namespace GreeterLib
{
public class GreeterService : IGreeterService
{
public string Greet(string name)
{
return string.Format("Hello, {0}", name);
}
}
}
This is hosted for debug purposes in a console as follows:
using System;
using System.ServiceModel;
using GreeterLib;
namespace GreeterConsole
{
class Program
{
static void Main(string[] args)
{
using (ServiceHost serviceHost = new ServiceHost(typeof(GreeterService)))
{
serviceHost.Open();
Console.WriteLine("WCF Service is running...");
Console.ReadLine();
serviceHost.Close();
}
Console.WriteLine("WCF Service has closed");
}
}
}
And as always with WFC, the config file does a lot of the heavy lifting of tying things together:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
</bindings>
<services>
<service name ="GreeterLib.GreeterService" behaviorConfiguration="Default">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/greeter"/>
</baseAddresses>
</host>
<endpoint
address=""
binding ="basicHttpBinding"
contract="GreeterLib.IGreeterService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="Default">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
So far so usual and SOAPy. I can run the console, and verify that at http://localhost:8080/greeter there is a service, it has wsdl at http://localhost:8080/greeter?wsdl, which specifies the soap that I would need to craft if I wanted to talk to it. I'm not going to do that manually.
To do this in a REST style, we change as follows:
The contract needs a WebGet attribute, specifying the URL to match:
using System.ServiceModel;
using System.ServiceModel.Web;
namespace GreeterLib
{
[ServiceContract]
public interface IGreeterService
{
[OperationContract]
[WebGet(UriTemplate = "greet/{name}")]
string Greet(string name);
}
}
The program needs to use WebServiceHost:
using System;
using System.ServiceModel;
using System.ServiceModel.Web;
using GreeterLib;
namespace GreeterConsole
{
class Program
{
static void Main(string[] args)
{
using (WebServiceHost serviceHost = new WebServiceHost(typeof(GreeterService)))
{
serviceHost.Open();
Console.WriteLine("WCF Service is running...");
Console.ReadLine();
serviceHost.Close();
}
Console.WriteLine("WCF Service has closed");
}
}
}
The config needs to use the webHttpBinding binding
<service name ="GreeterLib.GreeterService" behaviorConfiguration="Default">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/greeter"/>
</baseAddresses>
</host>
<endpoint
address=""
binding ="webHttpBinding"
contract="GreeterLib.IGreeterService" />
</service>
And there you go. You can now run this, and hit the URL http://localhost:8080/greeter/greet/Anthony
and get the response <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Hello, anthony</string>
You can also return data in JSON format by setting attributes.
But if you hack around a bit, you can also take full control of the XML returned, and serve some Plain Old XML if you want, as follows:
The contract must return a Message:
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Web;
namespace GreeterLib
{
[ServiceContract]
public interface IGreeterService
{
[OperationContract]
[WebGet(UriTemplate = "greet/{name}")]
Message Greet(string name);
}
}
The service:
using System.ServiceModel.Channels;
using System.Xml;
using System.Text;
using System.IO;
namespace GreeterLib
{
public class GreeterService : IGreeterService
{
public Message Greet(string name)
{
XmlReader xmlReader = MakeXml(name);
return Message.CreateMessage(MessageVersion.None, "", xmlReader);
}
private XmlReader MakeXml(string name)
{
StringBuilder buffer = new StringBuilder();
using (XmlWriter xmlWriter = XmlWriter.Create(buffer))
{
xmlWriter.WriteStartDocument();
xmlWriter.WriteStartElement("greeting");
xmlWriter.WriteAttributeString("type", "hello");
xmlWriter.WriteString(string.Format("Hello, {0}", name));
xmlWriter.WriteEndElement();
xmlWriter.WriteEndDocument();
}
TextReader reader = new StringReader(buffer.ToString());
return XmlReader.Create(reader);
}
}
}
And you'll ret the returned data:
<greeting type="hello">Hello, anthony</greeting>
So there it is: POX over REST served by WCF. And it wasn't very difficult.
|
|
|
|