I got a question in the mail today how to get started with EPiServer CMS as a developer. Here are my tips:

Bookmark and Share

Tags: ,

I got a question how to find what was wrong when you only got an error “Exception has been thrown by the target of an invocation. [The server committed a protocol violation The server response was: …]” from a junior developer and I want to share my answer with all of you since it can be a little tricky.

The Exception “thrown by the target of invocation” indicated that an error has occurred in code called by using invoke through dot net reflection. This technique is common when you have plug-ins (like scheduled jobs in EPiServer) and other late binding in run-time.

The text in square brackets are the original exception (caught by the Invoke() method) and gives a clue what goes wrong but it is usually not enough to find the error.

How to find more clues when debugging

As always when searching for the cause of a problem or error condition the first rule is to get more clues.  Usually this process consist of finding a reproducible test scenario that triggers the condition and hook up the debugger to see what is happening inside the application.

We must make sure it halts on thrown exceptions even if it outside our code to get a call stack that can help us. Open your options dialog and uncheck ”Enable Just My Code”.

clip_image002

With default settings the debugger will only break on uncaught exceptions. We need the debugger to break when an exceptions are thrown regardless of they are handled by a try-catch statement.

This can be changed in your Exceptions dialog (Ctrl+Alt+E). You can either set it to catch all managed exceptions as I have in the screen shot below or if you know what exception you are looking for you can halt on that one alone to minimize noise since it will probably halt on exceptions a lot of places that are normal before reaching your area of intrest.

clip_image004

With this setup we are now ready to run the test case again to trigger the error. If everything is correctly configured it will break on the exception and show you a call stack.

Use the debugger windows in Visual Studio to examine the Call Stack. Inspect parameters and Local Variables at each step in the call stack. This should give enough clues so to find what is causing the error.

How to find more clues when you do not have the source code

If the exception is raised in code that is not our own we can use several strategies to find out more. One of the best is to use Lutz Roeder’s .NET Reflector and click File>Open to load all assemblies in your web applications bin-folder. Search for the class or method and Disassemble the methods outside your code and get more clues. (Another way, if the exception is inside Microsoft code, is to configure VS to use Microsoft source server to download source code.)

clip_image006

Here is an example of when I caught a “Exception has been thrown by the target of an invocation.  [NullReferenceException: Object reference not set to an instance of an object.]“ and found TransformCategoryForExport in the call stack.

By analyzing the call stack we can usually also find the point in our own code that is starting the chain of calls causing the exception. I would recommend setting a break point there and rerunning the test case to see if the parameters to the call are invalid.

Need more help?

God luck with you bug hunting! If you are stuck you are welcome to contact me at INEXOR and buy my services.

Bookmark and Share

Tags:

Today’s tip is for all of you who want to share the same web.config among all developers in a project so you can check-in and handle changes in your Source Code Control Repository.

EPiServer installation directory and web.config

You might have noticed that the default installation path is different on 32-bit and 64-bit windows. Either you install EPiServer at “C:\Program Files\EPiServer” or “C:\Program Files (x86)\EPiServer”.

This is an issue if you have developers working on a project with both 32 and 64-bit windows since you have references to EPiServer installation directory in Program Files in your web.config.

But it can be easily solved by using a NTFS junction for developers running x64.

image

Run CMD Shell as an administrator and run:

cd "C:\Program Files"
mklink /j EPiServer "C:\Program Files (x86)\EPiServer"

Bookmark and Share

Everyone is talking about CoC (Convention over Configuration) and DRY (Don’t Repeat Yourself) today. The idea is to reduce the amount of repetitive, needless configuration and just keep what’s important.

imageHere is a graph with the number of lines in web.config for a clean installation of EPiServer CMS from version 4 to 6 (CTP2). Adding Community, Mail or other product will not make it better.

Complexity is making EPiServer CMS expensive to own

Handling web.config takes time and effort when you have to merge changes between different environments during deployment and each version is adding more and more configuration to your web.config-file.

And it increases the risk or mistakes because important settings disappears in all noise from comments and default settings.

What can be done?

Here are my suggestions and I urge EPiServer to implement the before they release EPiServer CMS 6:

  • Comments: -246 lines. Just moving all comments and example settings to a separate file that is overwritten with the latest version during upgrade.
  • Oracle: -60 lines. Why not a separate sample file for oracle?
  • EPiServer UI-settings. –78  lines. Why not mount user interface folders using IIS virtual folders? Then you could move a lot in location tags to other web.config files!
  • Workflow: –42 lines. Not used by many. Why not put it in the sample file for people to include if they want it or start it with default values.
  • RemoteEvents: –32 lines. WCF Service configuration – Why not have a programmatic or shared database config
  • siteSettings: A line over 1500 characters long. Why not remove the default values and make it shorter?
  • Providers: –8 lines. Skip adding providers that are not used. Just keep the other in the sample file.

Enough whining for today :-)

Bookmark and Share

The Event Management System in EPiServer CMS 5 is used to propagate information that a page has changed to other web servers connected to the same database.

This is very important to setup if you have more than one web server or an enterprise site with several start pages because if it does not invalidate cache, the other web sites can potentially return old versions of a page!

Pitfall – Default configuration is using Multicast UDP!

If you read Configuring EPiServer CMS 5 R2 SP2 Enterprise you will learn that all you have to do is to set enableRemoteEvents="true" in web.config.

It is only mentioned briefly that it is using UDP and you may have to change your firewall settings. Too be more specific the default configuration is using multicast UDP broadcast! Tell this to your network guys and watch their reaction…

If you look around you will also find a tech note about the Event Management System Specification and a FAQ about setting up a server to use TCP protocol but not all clues needed are found there – you have to dig with the reflector to find out the rest.

Scenario: 4 start pages, 8 Front-end servers IN DMZ and 2 in LAN

You can change how the Event Management System communicates by changing WCF settings in web.config. It is possible to use TCP instead of UDP and also specify ports to use.

First, the current release (5 R2 SP2) does not support WCF Port Sharing so we need one port opened in the firewall for each start page (that is when you have several siteSettings-tags in web.config).

In our case we need to get four ports (for example port 13000-13003) opened in the firewall for TCP traffic between servers in DMZ and LAN.

Use the sample configuration below as a template to get it to work. In production I suggest using configSource-attribute in web.config to keep the WCF-settings in separate files.

Web.Config

<system.serviceModel>
  <services configSource="system.serviceModel.services.config" />
  <client configSource="system.serviceModel.client.config" />
  <behaviors>
    <serviceBehaviors>
      <behavior name="DebugServiceBehaviour">
        <!--TODO: the option should be only in test environment true in the production should be false-->
        <serviceDebug includeExceptionDetailInFaults="true" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <bindings>
    <netTcpBinding>
      <binding name="RemoteEventsBinding">
        <security mode="None" />
      </binding>
    </netTcpBinding>
  </bindings>
</system.serviceModel>

system.serviceModel.services.config

Use this to configure WCF Services (listens to incoming messages). Setup one service for each EPiServer site-tag.

  • Name should be unique.
  • Name must be the value of episerver/sites[siteid] plus "/EPiServer.Events.Remote.EventReplication", i.e. if siteId is “sss.se” then name should be “sss.se/EPiServer.Events.Remote.EventReplication”.
  • Each site must use a diffrent port in services/service/endpoint[address].
  • Use "localhost" in the address attribute to bind to all NIC on the machine.
<services>
  <service name="sss.se/EPiServer.Events.Remote.EventReplication" behaviorConfiguration="DebugServiceBehaviour">
    <endpoint name="RemoteEventServiceEndPoint" contract="EPiServer.Events.ServiceModel.IEventReplication" bindingConfiguration="RemoteEventsBinding" address="net.tcp://localhost:13000/RemoteEventService" binding="netTcpBinding" />
  </service>
  <service name="sss.no/EPiServer.Events.Remote.EventReplication" behaviorConfiguration="DebugServiceBehaviour">
    <endpoint name="RemoteEventServiceEndPoint" contract="EPiServer.Events.ServiceModel.IEventReplication" bindingConfiguration="RemoteEventsBinding" address="net.tcp://localhost:13001/RemoteEventService" binding="netTcpBinding" />
  </service>
  <service name="sss.dk/EPiServer.Events.Remote.EventReplication" behaviorConfiguration="DebugServiceBehaviour">
    <endpoint name="RemoteEventServiceEndPoint" contract="EPiServer.Events.ServiceModel.IEventReplication" bindingConfiguration="RemoteEventsBinding" address="net.tcp://localhost:13002/RemoteEventService" binding="netTcpBinding" />
  </service>
  <service name="sss.com/EPiServer.Events.Remote.EventReplication" behaviorConfiguration="DebugServiceBehaviour">
    <endpoint name="RemoteEventServiceEndPoint" contract="EPiServer.Events.ServiceModel.IEventReplication" bindingConfiguration="RemoteEventsBinding" address="net.tcp://localhost:13003/RemoteEventService" binding="netTcpBinding" />
  </service>
</services>

system.serviceModel.client.config

And each server that changes the content must also be aware of where to send notifications so we must list all servers and websites.

  • Name should be unique.
  • It is better if address contains IP-addresses than hostnames.
  • There should be one endpoint for each website on a server where port number matches the ports used in the services/service/endpoint[address].
<client>
  <!-- SERVER1 -->
  <endpoint name="sss.se-SERVER1" contract="EPiServer.Events.ServiceModel.IEventReplication" bindingConfiguration="RemoteEventsBinding" address="net.tcp://SERVER1:13000/RemoteEventService" binding="netTcpBinding" />
  <endpoint name="sss.no-SERVER1" contract="EPiServer.Events.ServiceModel.IEventReplication" bindingConfiguration="RemoteEventsBinding" address="net.tcp://SERVER1:13001/RemoteEventService" binding="netTcpBinding" />
  <endpoint name="sss.dk-SERVER1" contract="EPiServer.Events.ServiceModel.IEventReplication" bindingConfiguration="RemoteEventsBinding" address="net.tcp://SERVER1:13002/RemoteEventService" binding="netTcpBinding" />
  <endpoint name="sss.com-SERVER1" contract="EPiServer.Events.ServiceModel.IEventReplication" bindingConfiguration="RemoteEventsBinding" address="net.tcp://SERVER1:13003/RemoteEventService" binding="netTcpBinding" />
  <!-- SERVER2 -->
  <endpoint name="sss.se-SERVER2" contract="EPiServer.Events.ServiceModel.IEventReplication" bindingConfiguration="RemoteEventsBinding" address="net.tcp://SERVER2:13000/RemoteEventService" binding="netTcpBinding" />
  <endpoint name="sss.no-SERVER2" contract="EPiServer.Events.ServiceModel.IEventReplication" bindingConfiguration="RemoteEventsBinding" address="net.tcp://SERVER2:13001/RemoteEventService" binding="netTcpBinding" />
  <endpoint name="sss.dk-SERVER2" contract="EPiServer.Events.ServiceModel.IEventReplication" bindingConfiguration="RemoteEventsBinding" address="net.tcp://SERVER2:13002/RemoteEventService" binding="netTcpBinding" />
  <endpoint name="sss.com-SERVER2" contract="EPiServer.Events.ServiceModel.IEventReplication" bindingConfiguration="RemoteEventsBinding" address="net.tcp://SERVER2:13003/RemoteEventService" binding="netTcpBinding" />
  <!-- Repeat pattern above for all other servers and sites -->
</client>

Good luck! And let me know if this helps you or if you get stuck…

Credits to Petter Klang at EPiServer Support and Shahram Shahinzadeh on the backend dev team that has been helping us out.

Bookmark and Share

Tags: , , , ,

« Older entries § Newer entries »