Hi,
Just a quick note that I have changed blogging engine, the blog is still available under the same url http://blog.fohjin.com/ and I'll be moving the old posts over one by one.
One thing to note is that the new engine is a work under process and thus will not have all the nice features like adding comments and so on. New features will also come one a the time.
-Mark
Sunday, February 8, 2009
Blog has been changed
Monday, December 8, 2008
Hire the funny guy
I have seen several posts about this topic, among them one from Joel Spolsky Exploding Offer Season and his article on Inc.com from a while back. And there are the re-occurring posts where bloggers ask their readers if anybody would be interested in coming to work for them. I started to think a little bit what good qualities you want in a Developer.
And I say hire the funny guy, and I don’t mean the guy that can tell 100k different jokes, I mean the guy that reacts to his environment in a funny / smart way. It takes creativity, knowledge, people skills and the ability to quickly find an answer to a problem, the basic skills you would want in a developer. So when you have a choice between two equally qualified developers, choose the funny one, besides the already mentioned benefits it is also very pleasant to work with them :)
-Mark
Saturday, November 29, 2008
Linq is Cool, but…
Linq is great, got to love its ability to query object structures. In the following examples that I’ll show you I am only touching the abilities of Linq. And as you can see it is capable of doing some really cool things, but it comes at a price…
But first here is the basic object structure I use in the example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | using System.Collections.Generic; namespace LinqIsCool { public class DataStructure { public readonly Dictionary<string, Author> Authors; public readonly List<Blog> Blogs; public readonly Dictionary<string, Post> Posts; public DataStructure() { Authors = new Dictionary<string, Author> { {"mark", new Author("Mark Nijhof")}, {"mona", new Author("Mona Nijhof")}, {"milo", new Author("Milo Nijhof")}, {"thalia", new Author("Thalia Nijhof")} }; Posts = new Dictionary<string, Post> { {"post0", new Post("Tech talk 1", Authors["mark"])}, {"post1", new Post("Tech talk 2", Authors["mark"])}, {"post2", new Post("Tech talk 3", Authors["mark"])}, {"post3", new Post("Family talk 1", Authors["mona"])}, {"post4", new Post("Tech talk 4", Authors["mark"])}, {"post5", new Post("Family talk 2", Authors["mona"])}, {"post6", new Post("Tech talk 5", Authors["mark"])}, {"post7", new Post("Tech talk 6", Authors["mark"])}, {"post8", new Post("Family talk 2", Authors["mark"])}, {"post9", new Post("Family talk 3", Authors["milo"])} }; Blogs = new List<Blog> { new Blog("Blog.Fohjin.com", Authors["mark"]) { Posts = new List<Post> { Posts["post0"], Posts["post1"], Posts["post2"], Posts["post4"], Posts["post6"], Posts["post7"], Posts["post8"] } }, new Blog("KidsTalk.Nijhof.com", Authors["mark"]) { Posts = new List<Post> { Posts["post9"] } }, new Blog("Family.Nijhof.com", Authors["mona"]) { Posts = new List<Post> { Posts["post3"], Posts["post5"], Posts["post8"], Posts["post9"] } } }; } } public class Author { public string AuthorName { get; set; } public Author(string name) { AuthorName = name; } } public class Blog { public string BlogName { get; set; } public Author Owner { get; set; } public IList<Post> Posts { get; set; } public Blog(string name, Author owner) { BlogName = name; Owner = owner; } } public class Post { public string Title { get; set; } public Author Author { get; set; } public Post(string title, Author author) { Title = title; Author = author; } } } |
Wow that was long. Now the code below will actually show you several examples of using Linq, look at the comments for an explanation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | using System; using System.Collections.Generic; using System.Linq; namespace LinqIsCool { public class LinqExamples { private readonly DataStructure dataStructure; public LinqExamples() { dataStructure = new DataStructure(); } /// <summary> /// Parses the data structure to find Authors that do not own a Blog /// How does it work: /// The Linq query is joining all the available Authors with the Blogs connecting /// the Author with the Blog.Owner place the results in blogAuthors. The /// DefaultIfEmpty operator supplies a default element for an empty sequence, /// which in our example means that if there is no matching Blog for an Author it /// will return null. Then we specify to only select the objects where blogAuthor /// is null, so in effect only selecing Authors that have no Blog /// </summary> public void GetAuthorsThatDoNotOwnABlog() { var authors = ( from author in dataStructure.Authors.Values join blog in dataStructure.Blogs on author equals blog.Owner into blogAuthors from blogAuthor in blogAuthors.DefaultIfEmpty() where blogAuthor == null select author ).ToList(); authors.ForEach(x => Console.WriteLine("- " + x.AuthorName)); } /// <summary> /// Parses the data structure to find the number of Blogs an Authors owns /// How does it work: /// Selects every Author from the dataStructure.Authors.Values list and adds a Blogs /// List that gets its content from a sub Ling Query that basically selects all the /// Blogs from the current Author and returns the Count for it /// </summary> public void GetNumberOfBlogsPerAuthor() { var authors = ( from author in dataStructure.Authors.Values select new { _Autor = author, _Blogs = ( from blog in dataStructure.Blogs where blog.Owner == author select blog ).ToList().Count() } ).ToList(); authors.ForEach(x => Console.WriteLine("- " + x._Autor.AuthorName + " owns " + x._Blogs + " blogs")); } /// <summary> /// Parses the data structure to find the actual Blogs an Authors owns /// How does it work: /// Selects every Author from the dataStructure.Authors.Values list and adds a Blogs /// List that gets its content from a sub Ling Query that basically selects all the /// Blogs from the current Author and returns the List /// </summary> public void GetBlogsPerAuthor() { var authors = ( from author in dataStructure.Authors.Values select new { _Autor = author, _Blogs = ( from blog in dataStructure.Blogs where blog.Owner == author select blog ).ToList() } ).ToList(); authors.ForEach(x => { Console.Write("- " + x._Autor.AuthorName + " owns "); x._Blogs.ForEach(y => Console.Write(y.BlogName + " ")); Console.WriteLine(); }); } /// <summary> /// Parses the data structure to find specific Posts that belong to a specific Blog /// How does it work: /// First the Linq query specifies that it will use both dataStructure.Posts and /// dataStructure.Blogs. Then it specifies that the Blog should be the same as /// selectedBlog, the next line specifies that the resulting Posts should be part of /// the selected Blog.Posts list and the final step is to specify that the resulting /// Posts should be part of the provided list requestedPostIds. Then it selects the Post /// </summary> public void GetSpecificPostsFromSpecificBlog() { var requestedPostIds = new List<string> {"post0", "post1", "post9"}; var selectedBlog = dataStructure.Blogs[0]; var selectedPosts = ( from post in dataStructure.Posts from blog in dataStructure.Blogs where blog == selectedBlog && blog.Posts.Contains(post.Value) && requestedPostIds.Contains(post.Key) select post.Value ).ToList(); selectedPosts.ForEach(x => Console.WriteLine("- " + x.Title + " by " + x.Author.AuthorName)); } /// <summary> /// Parses the data structure to find Posts from a specifies Author on a Blog where the Author is not the Owner /// How does it work: /// First the Linq query specifies that it will use both dataStructure.Posts and /// dataStructure.Blogs. Then it specifies that the Post should be the same as /// requestedAuthor, the next line specifies that the blog.Owner should not be part of /// the same as requestedAuthor and the final step is to specify that the resulting /// Posts should be part of the Posts list of the current Blog. Then is selects the /// Post and the Blog /// </summary> public void GetPostsFromSpecificAuthorWhereNotBlogOwner() { var requestedAuthor = dataStructure.Authors["mark"]; var selectedPosts = ( from post in dataStructure.Posts from blog in dataStructure.Blogs where post.Value.Author == requestedAuthor && blog.Owner != requestedAuthor && blog.Posts.Contains(post.Value) select new { post.Value, blog } ).ToList(); selectedPosts.ForEach(x => Console.WriteLine("- " + x.Value.Title + " by " + x.Value.Author.AuthorName + " on " + x.blog.BlogName)); } } } |
Now we finally get to the part where we talk about the downside of Linq queries, you really quickly run into the risk that your code becomes unreadable. So when you do write some complex Linq queries I would suggest you also write proper comments use understandable variable names and perhaps split the Linq query into multiple Linq queries. Below here is the output when running the methods:
GetAuthorsThatDoNotOwnABlog()
- Milo Nijhof
- Thalia Nijhof
GetNumberOfBlogsPerAuthor()
- Mark Nijhof owns 2 blogs
- Mona Nijhof owns 1 blogs
- Milo Nijhof owns 0 blogs
- Thalia Nijhof owns 0 blogs
GetBlogsPerAuthor()
- Mark Nijhof owns Blog.Fohjin.com KidsTalk.Nijhof.com
- Mona Nijhof owns Family.Nijhof.com
- Milo Nijhof owns
- Thalia Nijhof owns
GetSpecificPostsFromSpecificBlog()
- Tech talk 1 by Mark Nijhof
- Tech talk 2 by Mark Nijhof
GetPostsFromSpecificAuthorWhereNotBlogOwner()
- Family talk 2 by Mark Nijhof on Family.Nijhof.com
Just for fun I “Alt + Enter”-ed a couple times, did some manual formatting and viola you have the same results but now using Lambda, remember I said something about readability?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | using System; using System.Collections.Generic; using System.Linq; namespace LinqIsCool { public class LambdaExamples { private readonly DataStructure dataStructure; public LambdaExamples() { dataStructure = new DataStructure(); } /// <summary> /// Parses the data structure to find Authors that do not own a Blog /// How does it work: /// The Linq query is joining all the available Authors with the Blogs connecting /// the Author with the Blog.Owner place the results in blogAuthors. The /// DefaultIfEmpty operator supplies a default element for an empty sequence, /// which in our example means that if there is no matching Blog for an Author it /// will return null. Then we specify to only select the objects where blogAuthor /// is null, so in effect only selecing Authors that have no Blog /// </summary> public void GetAuthorsThatDoNotOwnABlog() { var authors = ( dataStructure.Authors.Values.GroupJoin( dataStructure.Blogs, author => author, blog => blog.Owner, (author, blogAuthors) => new {author, blogAuthors} ).SelectMany( @t => @t.blogAuthors.DefaultIfEmpty(), (@t, blogAuthor) => new {@t, blogAuthor} ).Where( @t => @t.blogAuthor == null ).Select( @t => @t.@t.author ) ).ToList(); authors.ForEach(x => Console.WriteLine("- " + x.AuthorName)); } /// <summary> /// Parses the data structure to find the number of Blogs an Authors owns /// How does it work: /// Selects every Author from the dataStructure.Authors.Values list and adds a Blogs /// List that gets its content from a sub Ling Query that basically selects all the /// Blogs from the current Author and returns the Count for it /// </summary> public void GetNumberOfBlogsPerAuthor() { var authors = ( dataStructure.Authors.Values.Select( author => new { _Autor = author, _Blogs = ( dataStructure.Blogs.Where( blog => blog.Owner == author ) ).ToList().Count() }) ).ToList(); authors.ForEach(x => Console.WriteLine("- " + x._Autor.AuthorName + " owns " + x._Blogs + " blogs")); } /// <summary> /// Parses the data structure to find the actual Blogs an Authors owns /// How does it work: /// Selects every Author from the dataStructure.Authors.Values list and adds a Blogs /// List that gets its content from a sub Ling Query that basically selects all the /// Blogs from the current Author and returns the List /// </summary> public void GetBlogsPerAuthor() { var authors = ( dataStructure.Authors.Values.Select( author => new { _Autor = author, _Blogs = ( dataStructure.Blogs.Where( blog => blog.Owner == author ) ).ToList() } ) ).ToList(); authors.ForEach(x => { Console.Write("- " + x._Autor.AuthorName + " owns "); x._Blogs.ForEach(y => Console.Write(y.BlogName + " ")); Console.WriteLine(); }); } /// <summary> /// Parses the data structure to find specific Posts that belong to a specific Blog /// How does it work: /// First the Linq query specifies that it will use both dataStructure.Posts and /// dataStructure.Blogs. Then it specifies that the Blog should be the same as /// selectedBlog, the next line specifies that the resulting Posts should be part of /// the selected Blog.Posts list and the final step is to specify that the resulting /// Posts should be part of the provided list requestedPostIds. Then it selects the Post /// </summary> public void GetSpecificPostsFromSpecificBlog() { var requestedPostIds = new List<string> {"post0", "post1", "post9"}; var selectedBlog = dataStructure.Blogs[0]; var selectedPosts = ( dataStructure.Posts.SelectMany( post => dataStructure.Blogs, (post, blog) => new {post, blog} ).Where( @t => @t.blog == selectedBlog && @t.blog.Posts.Contains(@t.post.Value) && requestedPostIds.Contains(@t.post.Key) ).Select( @t => @t.post.Value ) ).ToList(); selectedPosts.ForEach(x => Console.WriteLine("- " + x.Title + " by " + x.Author.AuthorName)); } /// <summary> /// Parses the data structure to find Posts from a specifies Author on a Blog where the Author is not the Owner /// How does it work: /// First the Linq query specifies that it will use both dataStructure.Posts and /// dataStructure.Blogs. Then it specifies that the Post should be the same as /// requestedAuthor, the next line specifies that the blog.Owner should not be part of /// the same as requestedAuthor and the final step is to specify that the resulting /// Posts should be part of the Posts list of the current Blog. Then is selects the /// Post and the Blog /// </summary> public void GetPostsFromSpecificAuthorWhereNotBlogOwner() { var requestedAuthor = dataStructure.Authors["mark"]; var selectedPosts = ( dataStructure.Posts.SelectMany( post => dataStructure.Blogs, (post, blog) => new { post, blog } ).Where( @t => @t.post.Value.Author == requestedAuthor && @t.blog.Owner != requestedAuthor && @t.blog.Posts.Contains(@t.post.Value) ).Select( @t => new { @t.post.Value, @t.blog } ) ).ToList(); selectedPosts.ForEach(x => Console.WriteLine("- " + x.Value.Title + " by " + x.Value.Author.AuthorName + " on " + x.blog.BlogName)); } } } |
Hope you enjoyed this.
-Mark
Wednesday, November 26, 2008
Being Specific with your Generics – Visitor Pattern without the Visitors
Generics are very useful when you have a particular functionality / logic that you want to apply on different types. Instead of having to duplicate the functionality for all your types you can put it in a generic class / method and have all the different types use it. One of the most used examples of this I think is it List<T> this is basically a strongly typed list that can be used for any type, and it works the same for any type.
This is great, but… (yes there is a but), what if what you have is 90% generic and 10% specific to the provided type, lets walk through an example that I encountered when working with Castle ActiveRecord. Each Business Data Object that you want to persist to the database using Castle ActiveRecord needs to be derived from ActiveRecordBase<T> where T is the derived class. This gives certain default functionality to each derived object like Save(). I’ll use the Blog and Post analogy that Ayende uses a lot too as it makes sense.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using Castle.ActiveRecord; namespace Fohjin.SpecificWithGenerics { [ActiveRecord] public class Blog : ActiveRecordBase<Blog> { [PrimaryKey] public virtual int Id { get; set; } [Property] public string Name { get; set; } [Property] public string Author { get; set; } [HasMany(typeof(Post))] public IList<Post> Posts { get; set; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | using Castle.ActiveRecord; namespace Fohjin.SpecificWithGenerics { [ActiveRecord] public class Blusing Castle.ActiveRecord; namespace Fohjin.SpecificWithGenerics { [ActiveRecord] public class Post : ActiveRecordBase<Post> { [PrimaryKey] public virtual int Id { get; set; } [Property] public virtual string PostId { get; set; } [BelongsTo("BlogId")] public virtual Blog Blog { get; set; } [Property] public string Title { get; set; } [Property] public string Content { get; set; } } } og : ActiveRecordBase<Blog> { [PrimaryKey] public virtual int Id { get; set; } [Property] public string Name { get; set; } [Property] public string Author { get; set; } [HasMany(typeof(Post))] public IList<Post> Posts { get; set; } } } |
Now that this is working fine, we wanted to add some generic functionality to both classes, a GetOrCreate (object id) method, which as the name already specifies tries to get an items based on the provided Id from the datebase and if that fails create a new one. So I created a RepositoryItemBase<T> class:
1 2 3 4 5 6 7 8 9 10 11 12 13 | using Castle.ActiveRecord; namespace Fohjin.SpecificWithGenerics { public class RepositoryItemBase<T> : ActiveRecordBase<T> where T : class, new() { public T GetOrCreate(object id) { return Find(id) ?? new T(); } } } |
So instead of having our Business Data Objects inherit from ActiveRecordBase<T> they will inherit from RepositoryItemBase<T> that inherits from ActiveRecordBase<T> again. Thus in affect extending the ActiveRecordBase<T> with the GetOrCreate functionality.
Now this works all fine, except that our Post class has a PostId which is not the PrimaryKey, but this is the key that is used to find the blog posts. This means that we need to specifically handle some generic types different than others. In my example I only have one extension and you could argue to add that method to the actual Business Data Objects, but in our solution we have many more Business Data Objects and there are only a few different GetOrCreate variants. And there are more generic extension methods. I could also create new derived objects from RepositoryItemBase<T> and have the specific functionality in there, but that didn’t look to appealing to me. So here is my implementation using an adapted Visitor Pattern using a bit like a very simple IoC container and Delegates.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | using System; using System.Collections.Generic; using Castle.ActiveRecord; namespace Fohjin.SpecificWithGenerics { public class RepositoryItemBase<T> : ActiveRecordBase<T> where T : class, new() { private readonly IDictionary<Type, Func<object, T>> delegatesByType; public RepositoryItemBase() { delegatesByType = new Dictionary<Type, Func<object, T>>(); InitializeDelegatesByType(); } public T GetOrCreate(object id) { if (!delegatesByType.ContainsKey(typeof(T))) throw new TypeLoadException("The provided type \"{0}\" is not supported!", typeof(T).ToString()); return delegatesByType[typeof(T)].Invoke(id); } private void InitializeDelegatesByType() { delegatesByType.Add(typeof(Blog), x => Find(x) ?? new T()); delegatesByType.Add(typeof(Post), x => { var posts = FindAllByProperty("PostId", x); if (posts.Length > 0) { return posts[0]; } return new Post { PostId = x.ToString() } as T; }); } } } |
As you can see I added a IDictionary<Type, Func<object, T>> this is for the IoC principle but instead of returning an object for a specific type it returns a function. This is where the Visitor Pattern comes into play, you define specific logic for each type, just like you would define a Visitor for each type, but instead of creating the different Visitor classes I defined different delegates with the same signature.
edit
I got some comments from people about something I did know, but maybe didn’t want to see :) the class RepositoryItemBase is violating the Open Closed Principle (OCP). As I also explain in a comment to Raymond we moved away from Castle ActiveRecord:
The main reason we were using Castle ActiveRecord was because we could configure our NHibernate mappings using Attributes instead of writing XML. But how Castle ActiveRecord places all the extra logic in the data containers was bothering us on different areas. I want the application to have a single point to do any data related tasks so I ended up with ugly methods that were like this: public class Repository { public void Save(ActiveRecordBase entity) { entity.Save(); } }
So I re-factored Castle ActiveRecord out of the solution and started using Fluent NHibernates AutoMap functionality for the mapping and DB schema generation (while developing) and plain NHibernate for the persistence tasks, much cleaner.
I also re-factored the non primary key name to be SourceId and placed this in the following interface:
1 2 3 4 5 6 7 | namespace Fohjin.SpecificWithGenerics { public interface IRepositoryItemWithSourceId { string SourceId { get; set; } } } |
Now all the Business Data Objects that have this special Id need to inherit from this interface, and that lets me do to following thing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using FluentNHibernate.Framework; using InfoDoc.Server.BL.BDO.Interfaces; using NHibernate; using NHibernate.Criterion; namespace Fohjin.SpecificWithGenerics { public class Repository : IRepository { private readonly ISessionSource sessionSource; public Repository(ISessionSource source) { sessionSource = source; } public IList<T> Query<T>(Expression<Func<T, bool>> function) { ISession session = sessionSource.CreateSession(); FluentNHibernate.Framework.IRepository nHibernateRepository = new FluentNHibernate.Framework.Repository(session); return nHibernateRepository.Query(function); } public T GetOrCreate<T>(object id) where T : class, IRepositoryItemWithSourceId, new() { var items = Query<T>(r => r.SourceId == id.ToString()); return items.Count > 0 ? items[0] : new T { SourceId = id.ToString() }; } } } |
As you can see now we are talking Generics again and don't have any dirty techniques to threat a generic value in a specific way. I do plan to explore the technique that Raymond suggested as well as a technique John suggested in a feature post, but for this problem the current solution is probably the best.
I hope you found this useful and as always if it can be improved please let me know.
-Mark
NNUG – TFS With Terje Sandstrøm
Last night was again a very interesting NNUG meeting this time with Terje Sandstrøm and the topic this day was TFS and how to use TFS in a Continuous Integration process. There were many things that I didn’t know yet and many Aha moments during the presentation. I also heard from other attendees that they would be applying the newly learned knowledge in their own environment right away.
And as usual the day didn’t end after the presentation, a couple of guys went with Terje to a pub to get some very interesting discussions going, I remember going home quite late. You can follow Terje on his blog http://geekswithblogs.net/terje/Default.aspx
-Mark
Tuesday, November 25, 2008
Announcing Isolator for Sharepoint : Unit testing for sharepoint made easier (and a free license!)
Typemock are offering their new product for unit testing SharePoint called Isolator For SharePoint, for a special introduction price. it is the only tool that allows you to unit test SharePoint without a SharePoint server. To learn more click here.
The first 50 bloggers who blog this text in their blog and tell us about it, will get a Full Isolator license, Free. for rules and info click here.
This is a great change to gain some experiance with Typemock
-Mark
Monday, October 27, 2008
Arnon Rotam-Gal-Oz talks about: ”Architect Soft Skills”
This is a very interesting article about the non technical skills an Architect should have: http://www.rgoarchitects.com/nblog/2008/10/26/ArchitectSoftSkills.aspx
Go ahead and read it.