Monday, December 27, 2010

EasySearch - “The XSLT stylesheet could not be loaded”

Right before christmas I got a “The XSLT stylesheet could not be loaded” error message from the EasySearch FacetCollection control on a customers test server. This confused me quite a lot as the FacetCollection control had worked without problems a couple of days earlier.

The first thing that came to mind when seeing this error message was access rights. Maybe the file permissions had been changed after the last deploy to the server? I checked this, and the file permissions were fine. So I checked the MIME types for .xsl and .xslt, but they were also correct. I suddenly realized that I was able to access the XSLT stylesheets by their url’s on the server, which meant that there had to be an access issue somewhere.

Unfortunately, I couldn’t find it. And to make matters worse, I couldn’t find the piece of code that returned the error message. So I ended up sending an email to NetworkedPlanet support. After asking me to check my file permissions (duh…), they found the issue.

The site was using Basic authentication! We had changed from Windows to Basic authentication a couple of days earlier, but apparently this line in the AbstractXmlControl.cs returns a HTTP 401 – Unauthorized exception if the site uses Basic authentication:

ret.Load(targetUri.ToString(), null, resolver);

Luckily, the site is supposed to use Windows authenication, so we simply switched back.

If you need to use Basic authentication, you can solve this issue by adding the XSLT stylesheet in the code behind:

FlatListFacet.XslStylesheet = File.ReadAllText(pathToXslt);

I’d like to thank the NetworkedPlanet support team for great service and resonse time!

Tuesday, December 7, 2010

EasySearch – Creating a custom index handler

Lately, I’ve spent quite a lot of time figuring out EasySearch. There is a developer guide available, but sadly it is quite messy and brief. So in this blog post I’ll show you how to create a custom index handler in order to add a creation year field to your Lucene document.

The first thing we need to do is to create a custom index handler class. This class needs to inherit from the NetworkedPlanet.EasySearch.Lucene.IIndexHandler interface, which means it needs two methods: IndexFile(…) and IndexPage(…):

public class CustomIndexHandler : IIndexHandler

{

  public CustomIndexHandler(){}

  public bool IndexPage(PageData page, Document luceneDoc)

  {

    AddYearField(page, luceneDoc);

    return true; // returns true, because this method should never block the indexing of a file

  }

 

  public bool IndexFile(UnifiedFile file, Document luceneDoc)

  {

    return true; // returns true, because this method should never block the indexing of a file

  }

 

  private void AddYearField(PageData page, Document luceneDoc)

  {

    // Add the last modified date of the file to a field named

    // "modified". Use a field that is indexed (i.e., searchable),

    // but don't tokenize the field into words.

    luceneDoc.Add(new Field("creationYear", DateTools.TimeToString(page.Created.Ticks, DateTools.Resolution.YEAR), Field.Store.YES, Field.Index.UN_TOKENIZED));

  }

}

The AddYearField adds a new “creationYear” field to the Lucene document. This field is given a year value, such as 2008, 2009, 2010. In order to invoke the index handler before the Lucene document is indexed in Lucene we need to register it in the <indexconfiguration> section in web.config:

<lucene:configuration>

  <lucene:indexhandler Type="MyProject.CustomIndexHandler, MyProject"/>

</lucene:configuration>

It’s as easy as that! You can now use the creationYear field in the Lucene document in a facet or in any other way you need.

Happy coding!

Friday, October 29, 2010

The CRM Plugin Registration Tool

After releasing the Epinova.CRMFramework, I’ve received a lot of questions regarding Microsoft Dynamics CRM to EPiServer communication.

A lot of customers want to store some of their CRM data in EPiServer. For example, if they create a new Account in CRM they automatically want a page to be created for this Account in EPiServer. Epinova.CRMFramework does not cover this, as it can only create, read, update and read information from EPiServer to CRM. When a new instance of an entity is created in CRM, EPiServer has no clue of this happening. You could of course create a scheduled job that checks for new instances of the entity every 5 minutes, but that would be a terrible solution! Don’t even think about doing that!

So what to do then? Luckily, a clever guy named Ajith has created a CRM Plugin Registration Tool. Using this tool you can register a plugin and attach it to the Save event of entities, and send the entities to a web service. The web service is of course a web service you’ve created in your EPiServer project.

I will blog more about this in detail when it’s not friday afternoon, this is only a short overview for those of you who need a solution instantly. 

Have a great weekend! :)

Wednesday, October 20, 2010

The alpha version of the open source Microsoft Dynamics CRM Framework for EPiServer is now available!

I’ve been working on a open source Microsoft Dynamics CRM Framework for EPiServer for a couple of months now. And I’m proud (and a bit nervous) to announce that it is now available: http://crmframework.codeplex.com/

I’ve tried to document the framework as thoroughly as possible, but there will definitely surface issues I haven’t documented thoroughly enough. So please feel free to contact me whether you have any questions, comments or feel for a chat :)

Tuesday, September 7, 2010

Technical thoughts on the open source Mirosoft Dynamics CRM Framework for EPiServer – Part 3

In part 1 and part 2 I gave an overview of how you can create, read, update and delete CRM entities, and the available methods of doing so. 

We’ve created a CrmContact entity class which we will continue to use in this last part of the blog series. But so far, we’ve only operated on 1:N relationships between entities. This blog post will focus on how you can use the CRM Framework in order to operate on N:N relationships between entities.

N:N Relationships in MS CRM

You can view an entities N:N Relationships from ‘Settings’ –> ‘Customization’ –> ‘Customize Entities’ –> Entity –> ‘N:N Relationships’.

ManyToMany

In the image above, you can see that the Contact entity has a N:N relationship with the Order entity. Now, how do we get all Orders associated with a Contact using this framework?

Creating entity classes

Let’s assume you already have a CrmContact entity class like the one created in part 1. For us to retrieve all Orders assiciated with a Contact entity, we also need to create a CrmOrder entity class:

[CrmEntity("salesorder")]

public class CrmOrder

{

[CrmAttribute("salesorderid")]

public Guid OrderId

{

get; set;

}

 

[CrmAttribute("name")]

public string Name

{

get; set;

}

}

I haven’t included a lot of attributes in this class, as I only want to display the name of the Orders. You should make it a habit of always including the entity’s primary key attribute (in this case OrderId) to your entity classes! This is because you will always need to specify the primary key attribute when doing update operations on an entity. So now we have both the entity classes we need: CrmContact and CrmOrder.

Creating a CrmManyToManyRelationshipController

In the two previous blog posts we’ve used CrmEntityControllers in order to do operations on entities. When working with N:N Relationships we use a different controller: CrmManyToManyRelationshipController<T, V>, where T is the entity we have and V is the entity we want to be returned. In this example we have a Contact entity, and want to retrieve all Orders associated with the Contact, to T is the CrmContact class and V is the CrmOrder class:

CrmControllerFactory factory = CrmControllerFactory.Instance();

CrmManyToManyRelationshipController<CrmContact, CrmOrder> manyRelationshipController =

factory.GetManyToManyRelationshipController<CrmContact, CrmOrder>();

Of course, in order to create a CrmManyToManyRelationshipController, we need a CrmControllerFactory as shown in line 1 of the code above.

Using the Find method

Now that we have our controller, we can use the Find method in order to retrieve all CrmOrders associated with the CrmContact:

CrmManyToManyRelationshipController<T, V>.Find(CrmQuery query) : List<V>

CrmManyToManyRelationshipController<T, V>.Find(CrmQuery query, CrmSortingCriteria sorting) : List<V>

CrmManyToManyRelationshipController<T, V>.Find(CrmQuery query, int page, int pageSize, out int totalCount, CrmSortingCriteria sorting) : List<V>

Example:

Guid contactGuid = new Guid("50F16A2B-904B-DE11-8A4B-0003FFD726F0");

 

CrmQuery query = new CrmQuery();

query.AddCondition(new CrmAttributeCriterion("contactid", contactGuid, CrmConditionOperator.Equal));

List<CrmOrder> orders = manyRelationshipController.Find(query);

What happens in the example above is that we find all Orders of the Contact with contactid == contactGuid. As mentioned in the previos blog post, the CrmQuery class will change at some point. I’m not sure if I’ll change it before the release of the alpha version, but at some point I will definitely improve it!

I still need a couple of testers, so if you’d like to try the framework before it is released, let me know :)

Thursday, September 2, 2010

Would you like to try the open source Microsoft Dynamics CRM framework for EPiServer?

I’ve been working part time on an open source Microsoft Dynamics CRM framework for EPiServer for a couple of months now. And the alpha version is almost ready!

And now it’s time for some testing! I’ve tested everything myself, and I’ve created unit tests for the framework. But still, I want some some more testing to be done before I make the source code public.

So if you’d like to try the framework and help me out at the same time, send me an email: karolikl (at) gmail (dot) com.

Prerequisites: An EPiServer site and Microsoft Dynamics CRM 4.0

After the testing and a code review, the source code will be released on codeplex. And of course, more blog posts are coming up!

Thursday, August 26, 2010

I’m joining the team at Epinova!

A lot of things are happening at the moment, and with those things comes great news: I’m joining the team at Epinova!

I’ve worked as a consultant at Itera Consulting (Objectware) for approximately 18 months now. I started fresh out of university, which means I’ve had a pretty steep learning curve.

In university, I had Java programming for my first two semesters and then moved on to C and scripting for the rest of my degree. For those of you who don’t know much about the University in Oslo, it’s all about Linux; Microsoft is considered the ‘dark side’. As a result of this I hadn’t touched an object-oriented language in two years when I started working for Itera Consulting. In addition, I’d never before touched .NET, C# or even Visual Studio (oh yes, I spent my entire time at university playing with Emacs). And worst of all, I had never heard of EPiServer.

So in the last 18 months or so, I’ve bent over backwards in order to learn everything I could about .NET, C# and EPiServer. And Itera Consulting has helped me learn in the best way possible, by throwing me head first into various EPiServer projects while at the same time holding my hand. My colleagues have always been eager to help me, teach me and at times laugh with me when I realize some of the strange things I’ve done.

As always, the hard work has paid off! I’ve simply fallen in love with EPiServer, its community and all that comes along with it. And I want more more more! So Epinova, you’re up next :) I look forward to joining your team and working alongside some of the best EPiServer developers in Norway!

Thursday, August 12, 2010

Technical thoughts on the open source Microsoft Dynamics CRM Framework for EPiServer – Part 2

In part 1 I gave an overview of how MS CRM entities and attributes, creating entity classes and creating CrmEntityControllers. This blog post will focus on the methods available through the CrmEntityController<T> class.

In the examples below we will assume that the CrmContact class from part 1 exists, so the only thing we need is a CrmControllerFactory and CrmEntityController<T> object:

CrmControllerFactory factory = CrmControllerFactory.Instance();

CrmEntityController<CrmContact> crmContactController = factory.GetEntityController<CrmContact>();

Creating a CRM entity

CrmEntityController<T>.Create(T entity) : Guid

Example:

CrmContact contact = new CrmContact();

contact.FirstName = "John";

contact.LastName = "Smith";

Guid contactGuid = crmContactController.Create(contact);

Reading a CRM entity

Get:

CrmEntityController<T>.Get(Guid entityId) : T

Example:

CrmContact contact = crmContactController.Get(guid);

FindAll:

CrmEntityController<T>.FindAll() : List<T>

CrmEntityController<T>.FindAll(CrmSortingCriteria sorting) : List<T>

Example:

List<CrmContact> contactList = crmContactController.FindAll();

 

List<CrmContact> sortedContactList = crmContactController.FindAll(new CrmSortingCriteria("firstname", CrmSortOrder.Ascending));

Find:

CrmEntityController<T>.Find(CrmQuery query) : List<T>

CrmEntityController<T>.Find(CrmQuery query, CrmSortingCriteria sorting) : List<T>

CrmEntityController<T>.Find(CrmQuery query, int page, int pageSize, out int totalCount, CrmSortingCriteria sorting) : List<T>

Example:

CrmQuery query = new CrmQuery();

query.AddCondition(new CrmAttributeCriterion("firstname", "John", CrmConditionOperator.Equal));

query.LogicalOperator = CrmLogicalOperator.Or;

query.AddCondition(new CrmAttributeCriterion("firstname", "Sarah", CrmConditionOperator.Equal));

 

List<CrmContact> contactList = crmContactController.Find(query);

 

List<CrmContact> sortedContactList = crmContactController.Find(query, new CrmSortingCriteria("firstname", CrmSortOrder.Ascending));

 

int totalCount;

List<CrmContact> pagedContactList = crmContactController.Find(query, 1, 10, out totalCount, null);

Updating a CRM entity

CrmEntityController<T>.Update(T entity)

Example:

CrmContact contact = crmContactController.Get(guid);

contact.FirstName = "Paul";

crmContactController.Update(contact);

Deleting a CRM entity

CrmEntityController<T>.Delete(Guid guid)

Example:

crmContactController.Delete(guid);

 

So these are the methods created so far, keep in mind that they might change! One of the things that definitely will change is the CrmQuery class. I’m not quite happy with that yet. So if you have any tips on how to create a good query system, please let me know :)

Wednesday, August 11, 2010

Someone just reminded me of “Push Your Limits”

A year ago I wrote a text for “97 things programmers should know” by O’Reilly. I decided not to write a technical piece, but rather a personal piece focusing on pushing your limits. The text did not make it into the book, but that doesn’t matter. What matters is that I hadn’t seen this text in a year until today when someone sent me the link. So just for fun I thought I’d post it here :)

Push your Limits

For many areas in life you need to know your limits in order to survive. Where it concerns personal safety, your limits define a boundary that should not be crossed. Where it concerns personal limitations, skills, and knowledge, however, knowing your limits serves an entirely different purpose. In programming, you want to know your limits so that you can pass them in order to become a better programmer.

Fortunately, not many programmers view their code as a ticket to their next paycheck. Those of us who are truly programmers at heart thrive on immersing ourselves in new code and new concepts, and will never cease to take an interest in and learn new technology. Whichever technology or programming language these programmers favor, they have one important thing in common: They know their limits, and they thrive on pushing them little by little every day.

We have all experienced that, if not armed with the knowledge we need or wished we had, attacking a bug or a problem head on takes a lot of time and effort. It can be too easy to pass the beast on to a colleague you know has the solution or can find one quickly. But how will that help you become a better problem solver? A programmer unaware of their own limits — or, worse, aware of them but not challenging them — is more likely to end up working in a never-ending loop of the same tasks every day. To insert a break into this loop, you need to acknowledge your limits by defining your programmatic weaknesses: Is your code readable by others? Are your tests sufficient? Focus on your weaknesses and you'll find that at the end of the struggle, you will have pushed that boundary an inch forward.

Needless to say, what others know should not be forgotten. Fellow programmers and colleagues offer a nearby source of knowledge and information which should be shared and taught to others. They are, however, ignorant of your limits. It is your responsibility to acknowledge exactly where your boundaries are hiding so that you can make better use of the knowledge they offer. One of the most challenging and, many would say, most effective ways to extend your own limits is to explain code and concepts to fellow programmers, for example by blogging or hosting a presentation. You will then force yourself to focus on one of your weaknesses, while at the same time deepening your own and others' knowledge by discussing and getting feedback from others.

At the end of the day, it is not about surviving. It is about surviving in the best way possible, by actively challenging yourself to excel as a programmer.

Monday, August 9, 2010

Technical thoughts on the open source Microsoft Dynamics CRM Framework for EPiServer – Part 1

Last week I announced that I’ve made up my mind about creating an open source MS CRM framework for EPiServer. The development has been going on since then, and in this blog post I’ll give some insight to what the framework will be like. Please keep in mind that the framework is in its alpha phase, and based on feedback, future code reviews and further development, the code might change! So consider this a draft :)

MS CRM entities and attributes

Before starting, it’s important to know the difference between entities and attributes. An entity such as Contact contains attributes such as Firstname and Lastname. Entities can be related to each other through one-to-many and many-to-many relationships. 

You can view all entities and attributes in ‘Settings’ –> ‘Customization’ –> ‘Customize Entities’ in CRM:

crm entities

You can view an entities attributes from ‘Customize Entities’ –> Entity –> ‘Attributes’:

crm attributes

Web.config

Integration with EPiServer is of course the main focus of this framework, so I’ve put all connection specific details in a separate configSection in the web.config file.

In <configSections>:

<sectionGroup name="crmFramework">

   <section name="crmConnector" type="Custom.CrmConnector.Configuration.CrmConnectorSection" />

</sectionGroup>

The configuration section itself:

<crmFramework>

   <crmConnector crmUrl="http://customer.crm.com:5555" username="UserName" password="Password" domain="Domain" organization="CrmOrg" />

</crmFramework>

Creating an Entity class in EPiServer

If you want to get an entity from CRM with all its attributes, this results in a lot of data being transferred and hence the operation can get time consuming. So in order for you to chose exactly what attributes you need, you need to create a class for each entity specifying its attributes. You can do CRUD operations on all CRM entities, standard or custom, and the framework supports all CRM attribute types.

The class needs a CrmEntity attribute speficying the CRM entity name. Each CRM attribute is defined as a public property with a CrmAttribute attribute specifying the attributes CRM name.

Here’s an example of a class created for the CRM Contact entity:

[CrmEntity("contact")]

public class CrmContact

{

// Has to be included for update operations

   [CrmAttribute("contactid")]

   public Guid ContactId

   {

      get;

      set;

   }

 

   [CrmAttribute("firstname")]

   public string FirstName

   {

      get;

      set;

   }

 

   [CrmAttribute("lastname")]

   public string LastName

   {

      get;

      set;

   }

}

Creating a CrmControllerFactory and CrmEntityControllers

In order to use the entity class we just created, we’ll need to get an instance of the CrmControllerFactory class. This is a singleton class handling authentication towards CRM and the CrmEntityControllers.

A CrmEntityController is a generic controller for doing operations on CRM entities. Through the controller you can access methods like Create, Delete, Find, Get, Update etc.

So let’s create a CrmControllerFactory and a CrmEntityController for the CrmContact class created above:

CrmControllerFactory factory = CrmControllerFactory.Instance();

CrmEntityController<CrmContact> crmContactController = factory.GetEntityController<CrmContact>();

As mentioned before, we can now use the crmContactController to do operations on CRM Contact entities. An example is getting a contact by Guid:

CrmContact contact = crmContactController.Get(new Guid("309B8DB0-C5DF-DC11-B07C-0003FF360638"));

string firstName = contact.FirstName;

All the methods available through the CrmEntityController<T> class will be covered in Part 2 of this blog series (in a couple of days). In the future, after a code review, I will also post some code showing the framework itself.

And remember, things might change! If you see anything you already disagree with, please let me know, I’m up for a discussion :)

Wednesday, August 4, 2010

Are you ready for an open source Microsoft Dynamics CRM Framework for EPiServer?


Since May I’ve been contemplating developing an open source Microsoft Dynamics CRM Framework for EPiServer. Then, a couple of weeks ago, I made up my mind. I’m going to do it!

Doesn’t that already exist though? Well, yes. EPiServer has its own EPiServer Connect for CRM which is useful in simple projects, but it has quite a lot of limitations when it comes to more complex cases. For instance, it only supports retrieving attributes of some CRM types and it’s limited to working with the Contact and Account entities. It is possible to extend the CRM Connector, but you will not be able to come clear of all the limitations. But EPiServer are not the only ones developing CRM frameworks, there are tons of them out there. The only problem is that most of them are too complex and not made for EPiServer. So my goal is to create a framework that can be used in complex projects where EPiServer Connect for CRM won’t do the trick, but the framework will be easy to use and most importantly: open source and free.

The development has already started and is coming along great if I may say so myself :) In phase 1 of the development I’m focusing on the following:

- CRUD operations on all types of entities (custom and standard)
- Read and update all types of entity attributes (custom and standard).
- Conditional search for entities
- And of course: Integration with EPiServer

I will keep on blogging while developing the framework, so you can expect the first technical blog post this weekend. That will give some insight to how you can use the framework and it will give you the opportunity to influence the development. I’m not quite sure when the framework itself will be available, but I’m hoping the Beta version is out in a couple of months (after all, I do have a job).

All opinions, wishes or slaps on the hand is highly appreciated!

Tuesday, July 20, 2010

Playing around with the EPiServer.Community.NewsFeed Namespace

 
In EPiServer Relate+, whenever you write a blog post, upload an image or become friends with someone this shows up on an activity feed. We all probably recognize this feature from Facebook, after all this is what Facebook is all about: Seeing what your friends have been up to lately.

When working with the EPiServer.Community.NewsFeed namespace, it’s important to understand the difference between Actor, Action, Target and Story.

Example 1: John uploaded an image
Example 2: John became friends with Sarah

Actor
Who performed the action. John is the actor in both the examples above.

Action
What has been done. In example 1 the action is “uploaded an image” while “became friends with” is the action in example 2.

Target
Who or what was the action done upon? In example 1, the image itself is the target. In example 2, Sarah is the target.

Story
If you put an Actor, an Action and a Target together, you have a Story. Both examples above are stories, the simplest stories we can create. If we want a more complex story we can create stories with multiple actors or targets.

Now how do we do this in code? Let’s assume you have a working Relate+ site. The users of your site will then be actors and the EPiServer Community entities will be your targets (for example blogs, images, users). So we only have to create actions and stories!

Creating, getting and removing actions

Creating an action is very easy, the only thing you need is to find a name for your action and to determine whether the action should be allowed to have multiple actors or targets. Let’s continue our Facebook line of thought and create a “Poke” action:

NewsFeedAction pokeUser = new NewsFeedAction("Poke User");

The action “Poke User” can only have one actor and one target. Let’s create another action “Poke Multiple Users”:

NewsFeedAction pokeMultipleUsers = new NewsFeedAction("Poke Multiple Users", NewsFeedActionCapability.MultipleTargets);

Removing an action is just as easy, first you get the action then you remove it:

NewsFeedAction pokeUser = NewsFeedHandler.GetAction("Poke User"); NewsFeedHandler.RemoveAction(pokeUser);

At last, you can get a collection with all the actions available by calling GetActions():

NewsFeedActionCollection actionCollection = NewsFeedHandler.GetActions();

Creating, getting and removing stories

Let’s create a NewsFeedStory specifying the action, actor and target:

NewsFeedStory story = new NewsFeedStory("Poke User", CurrentUser, null, pokedUser); NewsFeedHandler.AddStoryToActorFeed(story);

The AddStoryToActorFeed adds the story to the actors personal activity feed. In order to get a story you use the NewsFeedHandler.GetStory() method while removing a story can be done by using NewsFeedHandler.RemoveStory():

NewsFeedStory story = NewsFeedHandler.GetStory(1); NewsFeedHandler.RemoveStory(story);

As you can see, the GetStory() method expects you to know the id of the story you want to retreieve. This means that you need to loop through your stories if you don’t know the id. Let’s take a look at how that can be done!

A more complex example

Let’s assume John and Sarah have become friends on your community website. After weeks of cleaning up John’s messy code, Sarah has had enough and decides to delete John as a friend. On a standard installation of Relate+ the friendship will be deleted, but the story will still be visible in the newsfeed. So now we’d like write some code to delete the story from the newsfeed.

First we start off with a simple method called GetSpecificActions() which expects a list of action names. It loops though all the available actions on your site and returns a NewsFeedActionCollection containing all the actions specified in the list:

private NewsFeedActionCollection GetSpecificActions(List<string> includeActions)

{

    NewsFeedActionCollection newsFeedActionCollection = new NewsFeedActionCollection();

    foreach (NewsFeedAction action in NewsFeedHandler.GetActions())

    {

       if (includeActions.Contains(action.Name))

       {

          newsFeedActionCollection.Add(action);

       }

    }

    return newsFeedActionCollection;

}

Next we create a method called DeleteNewsFeedStory():

protected void DeleteNewsFeedStory(IUser actor, IUser target)

{

    List<string> actionList = new List<string> { Properties.Settings.Default.ActionFriendship };

    NewsFeedActionCollection newsFeedActionCollection =  GetSpecificActions(actionList);

    NewsFeedStoryCollection stories = NewsFeedHandler.GetActorFeed(actor, newsFeedActionCollection, DateTime.MinValue, DateTime.Now, 1, 25);

 

     foreach (NewsFeedStory story in stories)

     {

         foreach (IEntity targetEntity in story.Targets)

         {

             IUser user = targetEntity as IUser;

             if (user != null && user.ID == target.ID)

             {

                 NewsFeedHandler.RemoveStory(story);

             }

         }

     }

}

In this method we call GetSpecificActions() and use the NewsFeedActionCollection returned in order to find all Sarah’s stories with action equal to ActionFriendship. We then loop through all the stories and check if the one of the stories has John as a target, and if it does we delete the story.

We could do the exact same thing as we’ve done in this example without specifying the type of action the stories retrieved should have. You’d then get all stories (no matter what action) and you would have to check if the action was correct before removing the story.

Note: This example only shows how to delete the story, the friendship itself is not deleted in this example. I’ve made an assumption that you’ve already deleted the friendship prior to deleting the story :)

Thursday, April 29, 2010

Computers alone would not make me stay in this business.

Now and then I find myself observing other developers in awe of their discussions and line of thought. When in this state of mind, I don’t participate in the discussion because my mind is occupied by “something” I haven’t been able to put into words until recently.

Arnold Schwarzenegger once said “Good things don’t happen by coincidence” (yes, I’m quoting The Terminator). He is, however, wrong. If it hadn’t been for a series of coincidences, I would never have become a developer. In fact, if you would have asked me five years ago what I wanted to be when I “grew up”, I would have answered a doctor. Luckily for all patients out there, that didn’t happen.

As I began my degree in computer science, I didn’t actually know what programming was. Computers had never fascinated me, they were simply tools for getting things done more efficiently. Developers were geeky men whose words I couldn’t make sense of. Exactly how and when I went from being clueless to becoming one of the geeky men (although I’m female), I don’t know. But within the last year, I’ve become more and more certain of one thing: Computers alone would not make me stay in this business.

Of course, I find the technology we’re working with extremely interesting. But that can not be compared to the fascination I have of developers. When you, as a developer, are given a task you find challenging and exhilarating you become passionate about your code. You find yourself yelling at your computer when nothing works out the way you planned. You’re so determined to fix the problems that you’re not able to think outside the box and you end up leaving the office annoyed because you weren’t able to locate the problem. You wake up the next day even more determined than the day before, and after hours of intense work you’re able to think outside the box and the problems are solved. You’re not yelling anymore, you’re cheering.

Hopefully, this is not what every day looks like to you, but I’m sure you’ve experienced it. Have you, however, thought about what this scenario would look like if you weren’t passionate? You would probably not care much about these unexpected problems, you’d be satisfied as long as you could go home at night without any responsibilities. Luckily I can’t say I’ve met any developer yet who just doesn’t care, and I’m glad for that.

However, the passion goes further than this. The passion you have for your job is one thing, but what really amazes me is the passion I observe while I’m not at work. A lot of developers have their own personal projects where they live out their dream of trying out the newest technology in order to realize some of the ideas they get from who knows where. I see these personal projects being mentioned everywhere: on Twitter, in blogs, at meetups, or while talking to other developers. The passion these developers have for their projects sometimes intimidate me. How do they do it? How do they manage to fit all these projects into their schedule?

How or why they do it is not important, the only thing I ask of you is to continue! After all, that’s what makes me stay in this business: Your passion. So if you’re a developer, give yourself a pat on the back. You deserve it :)

Monday, March 8, 2010

Extending the ProfileBase class in ASP.NET

In this blog post I'll show you how you can extend the ProfileBase class in ASP.NET. This is useful in many situations, for example if you need to create a custom shopping cart. However, as a shopping cart can be quite complex, I've chosen to use a simpler example in this case.

Assume you have a website containing a large number of news articles and you want to let your authenticated users add personal comments to the each of the articles. By personal comments I mean comments that are stored in a user's profile and are therefore only accessible to the user who created the comment.

Our first step is to create a Comment class. Note that the Comment class has to be serializable.

    [Serializable]
public class Comment
{
private readonly int _articleId;
private readonly string _commentText;
private readonly DateTime _created;

public Comment(int articleId, string commentText)
{
_articleId = articleId;
_commentText = commentText;
_created = DateTime.Now;
}

public string CommentText
{
get { return _commentText; }
}

public int ArticleId
{
get { return _articleId; }
}

public DateTime Created
{
get { return _created; }
}
}



The second class we need is a CommentCollection class. This class contains a list of a specific users comments, as well as methods for adding, removing and editing comments. To keep it short, I’ve only included the InsertComment() method in this blogpost.



    [Serializable]
public class CommentsCollection
{
private readonly List<Comment> _comments;

public CommentsCollection()
{
if (_comments == null)
{
_comments = new List<Comment>();
}
}

public Collection<Comment> Comments
{
get
{
return new Collection<Comment>(_comments);
}
}

public void InsertComment(int articleId, string commentText)
{
Comment comment = new Comment(articleId, commentText);
_comments.Add(comment);
}
}


So now we have a class for managing the comments, but how do we use this? First of all, we have to register it as a property in the <system.web><profile><properties> section of the web.config:



<add name="CustomComments" type="Your.Namespace.CommentsCollection" serializeAs="Binary" />



Now it’s time to create a CustomProfile class, extending the Profilebase class. This class contains two methods. One for retrieving the Profile of the currently logged-in user, and one for retrieving the Profile of a specific user.



    public class CustomProfile : ProfileBase
{
/// <summary>
/// Get the profile of the currently logged-on user.
/// </summary>
public static CustomProfile GetProfile()
{
return (CustomProfile)HttpContext.Current.Profile;
}

/// <summary>
/// Gets the profile of a specific user.
/// </summary>
public static CustomProfile GetProfile(string userName)
{
return (CustomProfile)Create(userName);
}

/// <summary>
/// Gets a users comment collection
/// </summary>
public CommentsCollection CommentsCollection
{
get { return (CommentsCollection)GetPropertyValue("CustomComments"); }
}
}


We also have a property called CommentCollection that uses ProfileBase.GetPropertyValue to get the value of the CustomComments property registered in the web.config. Additionally, in order for ASP.NET to use your custom class instead of the ProfileBase class, this has to be specified in the web.config:



<profile enabled="true" defaultProvider="SqlProfile" automaticSaveEnabled="true" inherits="Your.Namespace.CustomProfile">



And that’s it! Here’s an example of how to use CustomProfile and CommentCollection to create comments:



     //Get the profile of the currently logged-in user
CustomProfile.CustomProfile profile = CustomProfile.CustomProfile.GetProfile();

// Get the users comment collection
CommentsCollection commentsCollection = profile.CommentsCollection;

// Insert new comment
commentsCollection.InsertComment(1, "My first comment");

// Get a users comments
Collection<Comment> comments = commentsCollection.Comments;


Note: There seems to be a bug in ASP.NET, so if you get the error “Unable to case object of type ‘ProfileCommon’ to type ‘CustomProfile’”, change <profile enabled="true">

to <profile enabled="false"> and back again to true. After this everything will work fine. If you find the cause of this bug, please let me know :)