Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

Sharing Entities between WCF and Silverlight

Pete Brown - 13 July 2009

One thing that often comes up when developing Silverlight applications is the desire to share entity classes between Silverlight and server-side code in WCF.

.NET RIA Services handles the sharing for us automatically. But you’ll find yourself needing the functionality in cases where you’re simply not using .NET RIA Services.

Sharing in WCF happens by default when you mark up your entities using [DataContract] and [DataMember] attributes. However, I find polluting my classes with those to be really ugly, as well as limiting. I also dislike the handling of namespaces on the client. Finally, that approach doesn’t serialize methods, just property values, so you can’t have computed properties or helper methods.

Instead, the approach we take is to cross-compile the classes in Visual Studio, and simply let the runtime use the client-side and server-side representations as appropriate. It’s very easy to set up, and once you start doing it this way, you’ll probably never want to go back to the generated approach.

With a little additional tooling help, this is what Prism does for code sharing. I often hear of folks who want to use Prism simply because they think it magically provides WPF/Silverlight code sharing, but it’s really just a part of the VS tooling, with a little help from a synchronization add-in provide with Prism.

Note that this approach will work for any code you need to share between the client and server or full CLR and Silverlight CLR.

The first step is to create a solution that has both the service and the Silverlight client. You can do this without the service in the solution, but you’ll have to bump out to the service project to sync compiles with the client. It’s just easier to have them both in the same solution.

image

Create the Entities Projects

The next step is to create the class library projects that will hold the entities. Since I spend most of my time working in the Silverlight client project, I typically create the Silverlight version first, and let it be the project of record, but doing it the opposite way works just fine as well.

Name the Silverlight class library the name you’d normally give it, but append “Silverlight” to the end. Similarly, append something like “Web” if you’re creating the server-side project.

Be sure to delete the default Class1.cs files from both projects.

image

The next step is to unify the default namespace between the two projects. This is one reason why you deleted the default class files – it’s easier to sync things that way. Open the project properties window for the entities projects.

image

image

Leave the assembly names alone (to avoid confusion), but set the default namespace to be the same in both cases. If you named your projects with the same root, all you need to do is remove the .Silverlight or .Web from the end.

Add Entity Classes

The next step is to add some entities to one of the projects. I’m going to leave the Silverlight project as the primary holder of the entities, and will therefore add the classes there. Again, either way will work.

using System;
using System.Net;

namespace PeteBrown.CodeSharing.Entities
{
    public class Employee
    {
        public string LastName { get; set; }
        public string FirstName { get; set; }
        public DateTime DateOfBirth { get; set; }
        public string EmailAddress { get; set; }
        public Uri WebSite { get; set; }
    }
}

Note that the class doesn’t have any of the usual markup cruft you need to deal with when defining entities that you’ll use in WCF. These are POCOs (Plain Old CLR Objects) and therefore could have been generated from anything from hand-coding to an ORM.

You’ll need to remove the System.Windows.* namespaces that don’t apply to a cross-CLR entity. That’s the one downside with starting with the Silverlight project. I’ve asked the SL team to either keep those out of the Class template, or provide an alternate class file template for Silverlight v4.

image

Once you have your class added to one project, the next step is to link it to the other project.

Adding Linked Classes

In the web project, select the option to add an existing item. Find the class you just added to the Silverlight project, and choose the Add option to “Add as Link”

image

This will link the Employee.cs file to the .web project. Changes made in the Silverlight project will automatically carry over to the linked project, as long as both are in the same solution.

image

Note that this does cause some issues with Source Safe. VSS, being the outdated and kludgy product it is (why do we still use it? why do my customers still use it?) requires some playing around to get it to properly link the two files rather than treat them as two distinct files.

Using the Shared Entity Class on the WCF Project

First, reference the Silverlight entity project from the Silverlight client project and the web entity project from the web server project. You need to do that before you create any services or, more importantly, add any service references. Make sure you get the references correct – C# will let you reference a Silverlight project from a full CLR project, but it gets messy once you start doing anything more than a vanilla class definition.

First, create the WCF service in your web project

image

Next, create a WCF method that returns the class, it doesn’t really matter what you do, as long as you get the entity class into the signature:

[ServiceContract(Namespace = "uri:com.irritatedvowel.demos.silverlight.codesharing")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class EmployeeService
{
    [OperationContract]
    public Employee GetEmployee()
    {
        return new Employee()
        {
            FirstName = "Pete",
            LastName = "Brown",
            DateOfBirth = new DateTime(1927, 01, 01),
            WebSite = new Uri("http://www.irritatedvowel.com", UriKind.Absolute),
            EmailAddress = "foo@bar.com"
        };
    }
}

 

In this case, I simply return a new hard-coded instance of the Employee class.

Using the Employee Class on the Client

Add a service reference as your normally would, but before clicking “OK”, you’ll want to double-check the code sharing settings. By default, the “add reference” dialog and process is set up to share any types that the client and server have in common. This is typically used for things like basic types, collections etc. but also works with your own classes.

In the service reference dialog, do all the reference steps as you normally would, but click the “advanced” button on the bottom left.

image

That will pop up this dialog. Note that I hate the approach of hiding useful stuff under an “Advanced…” button or tab – it discourages exploration and implies that the stuff behind that button or tab is somehow dangerous.

image

Once you have the window open, you have two choices here related to the topic of this post. You can either reuse all types in referenced assemblies (the default), of you can reuse only specific types. Leaving it on the default option is usually the prudent approach, as it requires less upkeep as you add on to the projects. We’ll follow that approach here.

Finish adding the service as normal.

You can now use the shared type on both the client and server, in the correct namespace. The entity type you’re using on the client isn’t buried under the service reference namespace as it would be if you used the usual approach of generating the client types based on the WSDL. This approach also enables sharing the same types between multiple service references without having to translate between them or otherwise have multiple confusing types defined on the client.

using PeteBrown.CodeSharing.Entities;
using PeteBrown.CodeSharing.Services;

namespace PeteBrown.CodeSharing
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();

            Loaded += new RoutedEventHandler(MainPage_Loaded);
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            var client = new EmployeeServiceClient();
            client.GetEmployeeCompleted += (s, ea) =>
                {
                    // do something, bind/whatever
                };
            client.GetEmployeeAsync();
        }
    }
}

 

Note that calling the service from code-behind is just for demo purposes here. My service calls are usually centralized in another class or performed directly from the ViewModel, depending on the complexity of the solution. Don’t fill up your code-behind with stuff like this. You can learn more about the ViewModel pattern by searching for MVVM or ViewModel

There’s one last step, and that has to do with adding custom code that may be platform-specific.

Alternate Implementations Based on Platform

My entities are usually pretty dumb: they’re just DTOs without anything that might be platform-specific. For that reason, I’m going to use a completely contrived example here, because I’d look at any realistic example and throw it away as “not how I’d do it” :)

Keep in mind, though, that this whole technique works for more than just entities, which is why I’m showing the conditional compilation approach here.

Silverlight projects automatically define the SILVERLIGHT conditional compilation symbol.

namespace PeteBrown.CodeSharing.Entities
{
    public class Employee
    {
        public string LastName { get; set; }
        public string FirstName { get; set; }
        public DateTime DateOfBirth { get; set; }
        public string EmailAddress { get; set; }
        public Uri WebSite { get; set; }

#if SILVERLIGHT
        public string ClientOnlyMethod()
        {
            return "I exist only on the client.";
        }
#else
        public string ServerOnlyMethod()
        {
            return "I exist only on the server";
        }
#endif

    }
}

 

Note that you don’t want to use conditional compilation for anything you want serialized. There may be instances where you can actually serialize if the types implement common interfaces, or if the serializer would otherwise know how to translate between them. You’ll need to test that on a case-by-case basis.

In the example above, I conditionally compile two methods. Since methods are just ignored by the serializer, there are no issues to contend with.

 

That’s it for sharing your code between the two platforms. The source for this project is available here.

       
posted by Pete Brown on Monday, July 13, 2009
filed under:        

44 comments for “Sharing Entities between WCF and Silverlight”

  1. Bart Czernickisays:
    I have been using this technique for a couple Silverlight 2.0/WCF REST projects...works really well.

    One thing that I will add (since you mention Serialization), its best to explicitly decorate the properties with the proper serialization attributes to "opt-in" for serialization.

    WCF 3.5 SP1 supports default POCO serialization, but it's regarded as not a good practice.
  2. Pete Brownsays:
    @Bart

    Why wouldn't it be a good practice? I think supporting POCOs is a huge benefit of doing it this way, and honestly, I despise marking up my classes with the WCF cruft. I actually consider POCO support to be a best practice vs. fine control of serialization.

    As an aside, RIA services is taking a similar approach with no required markup to handle serialization of entities, and for the same reason. If you support POCOs, then you greatly expand the potential sources for your classes.

    Pete
  3. Martin Nyborgsays:
    I can also recommend this pattern.

    I am working in a team of 6 developers converting a very large and very old :-) MS Access / SQL Server application with around 200 tables and a LOT of forms.

    We are using NHibernate and WCF and of course Silverlight 3.

    We have to use attributes on the classes to register well known types and also to hide some NH collection that are not serializable.

    But it works, and it works very well.
  4. Bart Czernickisays:
    @Pete,

    To me it is a matter of preference for the architect. However, to me I like to explicitly know what my application is doing. Compile that code in .NET 3.0 or 3.5 and it will not serialize. Compile that code in .NET 3.5 SP1 and it will serialize (POCO support). .NET 4.0 hopefully will continue to add POCO support. However, it's dependent on the framework version and it is clearer for me to express what I want and have the code compile in the same way.

    I guess you can write code like:
    bool test; // .NET makes it false (default)
    bool test = false; // I am explicitly stating it

    Lastly...while I have an opinion of my own and think for myself, I also recognize there are people much smarter than me that have been doing this for a lot longer. Two resources that I can point that recommend not doing this are (I read Juwal's book 4 times):
    Juwal Lowy - Programming WCF Services - page 105-106
    Jon Flanders - RESTful .NET - page 235

    They both have different reasons (which is actually different than mine). I don't want to post their reasons, but anyone who knows the Google Books hack can get to these pages :)
  5. Pete Brownsays:
    @Lixin

    This introduces no more coupling between the client and server than the traditional generated approach, plus it gives you more flexibility.

    If you don't want to expose the entities beyond your service facade / model / <insert layer du jour> you don't need to.

    @Bart
    Do those guys address SP1 and the supported POCO approach at all?

    I guess my thought is, if I have an entity that is being returned from my WCF service, I want it serialized. It seems pretty redundant to mark it up. Again, same approach as RIA Services, ADO.NET Data Services and others. Whereas the "bool test = false" example is good for documentation, I don't see how I get the same level of utility from the attributes.

    You can, of course, add all the attributes you want, but to me they add a lot of cruft that is buying me absolutely nothing of value.

    One point you made is, however, worth repeating: this only works with 3.5sp1 and above.

    FWIW, I don't know the google books hack; and I have never used google books :)

    Pete
  6. Miguel MAderosays:
    @Bart and @Pete, I think it depends on the use you give to your objects. If you're using DTOs then there's no point in using the attributes since you know that all of your properties are there for one and only one purpose, to be transfered. Altough if you're using DTOs, I don't see a lot of value on sharing assemblies.
    I like the DTO pattern and I'd recommend it, but it can also add some extra work. When we're sending objects with some logic, derived properties, etc I'd need to be explicit on what I'm sending.
  7. Pete Brownsays:
    I've read some snarky comments (twitter, email) lately about coupling -- the architectural masturbation topic-du-jour. Folks simply say "tight coupling is bad" and just lots of head-nodders while everyone goes back to business as usual.

    To be clear here, I am introducing no more coupling than you have when you use the auto-generated WCF types. You are free to use these as much or as little as those types.

    What this does give you is better control over namespaces, cleaner class definitions, and the ability to do things like have calculated properties, should you want to.

    If you desire to surface entities from your model, that can be perfectly valid, as long as you know the trade-offs.

    I've built systems where a single definition of a specific entity (like employee or account) was actually a very desireable approach. If you need to transform that for the viewmodel to suit a specific view, that's what the pattern is for.

    But to just spout out the slope-brow "Tight coupling, bad" argument is doing the community and your development team a disservice.

    Pete
  8. Lixinsays:
    <p>Hi Pete, What I normally experienced in the past was that when I changed something such as non-data-contract properties or internal fields, I never wanted the client side to sense the changes. There are some major advantages, </p><p>1. When the client side application was devloped by ourselves, we could always move the project into another solution if needed, without worrying about the loss of file links. </p><p>2. When the client side was developed by developers ouside our work environment, they would never have a chance to get any more information about the data objects than what we exposed to them. </p><p>By the way, my understanding towards those service proxy creating tools, including those in Visual Studio, is that they are different to direct referencing (such as file links in Visual Studio). </p><p>Although they do offer you client side classes by automatic class mapping, the service reference they create are all the ways contract based, and that really makes me comfortable and my work worryless. </p>
  9. Pete Brownsays:
    @Lixin

    Definitely legit concerns, especially when the server and client code are different dev projects, tracks, or teams.

    You could conceivably share only interfaces, but that's more work, and no real benefit over just the data contract-based approach.

    My approach stems more from the fact that most of the WCF services I've written or used in projects were purpose-built for the app, as opposed to shared across multiple apps. (especially in the case of Silverlight projects)

    Pete
  10. Carlsays:
    <p>Hi Pete, thanks for the article. I am in the process of using this method. One thing that I found help is that instead of #if SILVERLIGHT, I make the class a partial class and then extend it (both on the client and the server side) with partial classes. e.g. the definition is: </p><p>In Person.cs (linked and shared between assemblies) </p><p>public partial class Person </p><p>{ </p><p>// Common stuff </p><p>} </p><p>In Person.ServerOnly.cs (only on the server side assembly) </p><p>public partial class Person </p><p>{ </p><p>// Server Only Stuff stuff </p><p>} </p><p>In Person.ClientOnly.cs (only on the client side assembly) </p><p>public partial class Person </p><p>{ </p><p>// Client only stuff </p><p>} </p>
  11. Pete Brownsays:
    @Carl

    Nice approach. It nicely unclutteres the class for instances when you're sharing between multiple project types (maybe Silverlight in one, WCF in another, and a specialize part for WPF) and eliminates the silverlight-specific stuff from the server code.

    Pete
  12. Tomsays:
    Pete,

    Great article. I am brand new to WCF and just starting to make my way through the technology. So I am clear, the reuse types feature does not serialize the whole class (all methods and properties) just the data elements marked with the data member attribute?

    Thanks
  13. Carlsays:
    The one that that makes this approach a bit painful is that you must manually implement INotifyPropertyChanged and use ObservableCollections instead of IList/IEnumerable for you lists/collections.

    In the example you give above, it looks much cleaner and simpler than it is in practice. When you say '// do something/ bind whatever' - the only kind of binding that will work is OneTime Binding as properties are just {get;set;}. I suspect that you were just making it simpler to make the point clearer, but in some respects the point itself was that it was simple and clean.

    By in large, I love the concept. My main impetus to switch to it was to make validation work the same on the server and only the client (in my case Silverlight). But after about 8 hours of trying to move 60+ classes over to this approach, I may not get to the nerdvana that you describe.

    Please let me know if I am missing something.
  14. Pete Brownsays:
    @Carl

    Yes, one thing that the generated approach gives you is the ability to map into another type of collection, like ObservableCollection<T>.

    However, the default generated approach doesn't do anything for INotifyPropertyChanged. You were still on your own there. It's a problem that the ViewModel pattern helps to resolve by taking your model (entities) and mapping them into observable viewmodel entities, specific to a view.

    More work no matter how you dice it, unfortunately.

    FWIW, the way .NET RIA Services handles the client proxy generation takes all of this into account, as I recall.

    Pete
  15. Franklinsays:
    I am trying to use this approach and it all works until I do something like this:

    public class Es : ObservableCollection<example>
    {

    }

    or ...

    public class Es : INotifyCollectionChanged
    {

    #region INotifyCollectionChanged Members

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    #endregion
    }

    this causes compile errors.

  16. John Papasays:
    Hey Pete

    Just some thoughts to share ...

    I've done this approach, and the only issue I have run into that gave me pause is when I want to expand on my model in the Silverlight side. I don't like conditional compiles, so I tried using partial classes to expand on what I needed. Things such as adding DataAnnotations to the silverlight model only. It got a bit hairy after a bit since they truly had differences. Frankly, most of my apps have differences between the models. I'd love to be able to send validation logic up the chain but sans RIA Services its not a good option due to serialization issues.

    So another option I have been using on a project I joined is using mappers on the UI side to map the DTO's to a UI.Model. It works great, gives awesome decoupling and flexibility. BIG downside is now they have 3 models: the business layer, the DTO's and the UI.Model. YUK! Maint is not bad as you might think cuz once it is set up, it is no big deal. But dev stinks with it since they all have to be tweaked together when creating new models.

    There are variations in between I have used as well, the ideal one being where we can share the code between the layers and still keep our validation/metadata. That's one huge reason I am looking hopfeully at RIA Services in the future.
  17. CR35says:
    I am using this method in combination with Silverlight 3, and though it initially worked, but now I am getting the error 415, media not supported. My classes only contain String/int/datetime and [] of other classes as attributes. Is there a specific attribute that can give problems? But I didn't change the attributes since it stopped working.

    Any suggestions are welcome.
  18. Pete Brownsays:
    @CR35

    Did you change your WCF binding? That can cause this error. I strongly suspect this is a WCF configuration issue. However, in case it isn't:

    Your best bet is to roll back your changes until you get a working one, then see what changes break it. Classic debugging is about the only thing that will help you here. *Something* changed.

    Refresh/rebuild your service references as well. Also make sure all your classes in the two entity projects are linked, and you didn't skip any.

    string/int/datetime should be no problem, but the "other class" could contain anything, so I don't know what to tell you there.

    Pete
  19. CR35says:
    @ Pete.Brown

    I debugged my classes, firstly by making methods in the webservice that only involved certain classes and see when it breaks(it took a while before i discovered it only broke down on certain classes). I managed to isolate the problem to one class. When I compared the classes I noticed I didn't put a default parameterless constructor in that one. So I added one and the problem was fixed.

    Thanks for your response!
  20. Bobsays:
    Pete,

    Is there a way to get around the need to make sure that all of the properties/fields in the classes marked with the datamember attribute are editable? For example, I have a person class that has a read-only person id (primary key). I want to make sure that value cannot be changed.

    Thanks.
  21. Pete Brownsays:
    @Bob

    Unfortunately, for serialization, you'll need setters. You can have a private setter:

    public int Id
    {
    get { return _id; }
    private set { _id = value; }
    }

    but I dont' recall if that works on the client. If you try it before I get a chance to, please let me know the results.

    Pete
  22. Kevinsays:
    Hi Pete,
    Thanks for sharing your knowledge for me. I got an issue when I release your project to my web server. I didn't change anything. The project sometimes works, sometime not. I don't know why. Could you please take a look this error?
    The link is http://silverlight.allly.net/PeteBrown.CodeSharingTestPage.aspx

    IE browser always returns an error which comes from PeteBrown.CodeSharingTestPage page.
  23. Pete Brownsays:
    @Kevin

    That error doesn’t really tell you anything other than it had a hard time calling the service. You need to put an exception handler in your code and get the real inner exception.

    If it is intermittent, it could be a server-side problem. Maybe something with your configuration or your worker process setup?

    Pete
  24. Il_ya Tret_yakovsays:
    <p>For the INotifyPropertyChanged I use a class mediator.</p><p>public class MyMediator : INotifyPropertyChanged{</p><p>   private SharedClass myShare;</p><p>   public String Name{</p><p>       get{ return myShare.Name; }</p><p>       set{</p><p>           myShare.Name = value;</p><p>           RaisePropertyChanged("Name");</p><p>       }</p><p>   }</p><p>   ...</p><p>}</p>
  25. Eric says:
    Pete,

    Great article I am trying to implement this, but am running into a speed up. The ServiceReference is no longer generating the Service Client proxy in my Silverlight App. Any idea why?

    -Eric Kaufman
  26. Pete Brownsays:
    @Eric

    Any errors in the output window when you compile or update the reference? Are you referencing the correct DLLs in both projects?

    What happens when you remove the reference and re-add it? Do you see any error messages?

    Another possibility is it's there, but not in the namespace you thought it was in.

    As an aside, this is no longer necessary with Silverlight 4 and .NET 4. You'll be able to reuse your Silverlight DLLs directly starting with those versions.

    Pete
  27. Justinsays:
    Great article. I have implemented this in my WCF SilverLight project. I am running into issues when I am trying to pass an Entity that has a property of type Entity. Like below I am using Order with a property of Location. I thought I had all of the mark up correct but I get a runtime error. If I can't fix this I will just use simple data type paramaters, which is disappointing.

    #if SILVERLIGHT
    [DataContract]
    #else
    [DataContract, Serializable]
    #endif
    public class Order
    {
    public Order(int orderId, Location fromLocation, OrderStatus orderStatus)
    {
    this.OrderId = orderId;
    this.FromLocation = fromLocation;
    this.OrderStatus = orderStatus;
    }

    [DataMember]
    public int? OrderId { get; set; }
    [DataMember]
    public OrderStatus OrderStatus { get; set; }
    [DataMember]
    public Location FromLocation { get; set; }
    }

    #if SILVERLIGHT
    [DataContract]
    #else
    [DataContract, Serializable]
    #endif
    public class Location
    {
    public Location(int locationId, string locationName)
    {
    this.LocationId = locationId;
    this.LocationName = locationName;
    }

    [DataMember]
    public int LocationId { get; set; }
    [DataMember]
    public string LocationName { get; set; }
    }
    }
  28. Petesays:
    @Justin

    Put in a default constructor for both of those classes (constructor with no parameters) and let me know if that fixes it.

    Also, make sure you're including the OrderStatus type.
  29. Narendrasays:
    I tried this one in my project. Its not working in following scenario.
    Normal class library is using EnterpriseLibrary dll(some other references...) for data access. When i linked the same class in Silverlight project then system is showilng compilation errors. I can't add EnterpriseLibrary dll to silver light project.
    How can we handle that scenario? Waiting for earliest response.
  30. Simon Bsays:
    Hey Pete,
    I enjoyed reading this post. Looks like a useful shortcut to avoid mapping between Entities and DTOs.

    I'm not going to give my opinion on tight-coupling or explicit [DataContract] attributes etc.

    No one has brought up the impact of using Shared Types, as opposed to Shared Contracts. If I understand it correctly, Shared Contracts are how the proxy generation works by default. If you don't reference a shared assembly, the types are constructed on the client based on the contract exposed in the WSDL.

    So is the method in this blog post telling WCF to use the Shared Types serializer?

    See the following concise post for an explanation of them and the pros/cons for each:
    http://blogs.msdn.com/youssefm/archive/2009/04/15/an-overview-of-net-serializers.aspx

    Shared Types will allow more OO options such as inheritance hierarchies. But it comes at the cost of platform interoperability and performance.


  31. Petesays:
    @Simon

    Great info. Truthfully, I never looked into the differences in the serializer being used. If you do any testing and draw any conclusions, please be sure to update here so we can share with the community.
  32. Sameer Borikarsays:
    Hi Pete,

    Great article. Works perfect with entities returning basic data types.
    However, it fails during serializing Entities returning entities. I tried both options.

    1. Having default constructors for returning entities and containing entities
    2. Including containing entities as DataContract...

    It didn't work. I am facing the same problem as Justin.

    Any clue?


    [DataContract(Name = "LeMonTable")]
    public class LeMonTable
    {
    private List<LeMonColumn> _columns;
    private List<LeMonRow> _rows;
    public LeMonTable()
    {
    _columns = new List<LeMonColumn>();
    _rows = new List<LeMonRow>();
    }

    [DataMember]
    public List<LeMonColumn> Columns
    {
    get { return _columns; }
    set { _columns = value; }
    }

    [DataMember]
    public List<LeMonRow> Rows
    {
    get { return _rows; }
    set { _rows = value; }
    }

    public void AddColumn(LeMonColumn column)
    {
    _columns.Add(column);
    }

    public void AddRow(LeMonRow row)
    {
    _rows.Add(row);
    }
    }

Comment on this Post

Remember me

3 trackbacks for “Sharing Entities between WCF and Silverlight”

  1. NewsPeepssays:
    Thank you for submitting this cool story - Trackback from NewsPeeps
  2. DotNetShoutoutsays:
    Thank you for submitting this cool story - Trackback from DotNetShoutout