Sunday, June 24, 2012

Sharepoint bug: incorrect itemId in summary alerts with custom alert handler

Some time ago I faced with strange behavior. Custom alert template with custom alert handler was used for alerts for particular list. Immediate alerts worked successfully and custom alert handler was called properly. However summary daily alerts didn’t work.

Custom alert handler is inheritor of IAlertNotifyHandler interface which allows you to override default behavior of alerts processing. E.g. you may log all alerts with custom handler. Here are several examples which shows how to implement custom alert handler step by step: SharePoint – Customizing Alert emails using IAlertNotifyHandler, How To: Customizing alert emails using IAlertNotifyHandler. Shortly you need to override IAlertNotifyHandler.OnNotification method. It has single parameter of SPAlertHandlerParams type. In turn it has SPAlertEventData[] eventData property.

When custom alert handler processes immediate alert (which is caused e.g. by adding new item) eventData has single element and its itemId property contains integer identifier of the list item which caused alert. Using this identifier you may open list item in your handler and read its metadata, e.g. determine user who modified this item.

When summary alert is processed, eventData contains several items – separate item for each event. The problem is that if you will iterate through collection and get eventData[i].itemId you will get any except correct integer identifier (it can be 0, 186, 1932917208, etc., while real identifiers will be 10, 11, 12). For me such behavior looks like a bug.

This issue was mentioned also in this forum thread: SPAlertHandlerParams - not behaving correctly for daily alerts. And author Johnny Dogbert provided workaround:

   1: string url = ahp.eventData[i].itemFullUrl;
   2: int itemId = int.Parse(url.Substring(url.LastIndexOf('/') + 1).Replace("_.000", ""));

It works because in itemFullUrl for list items Sharepoint passes the following URL: testsite/Lists/Test list/10_.000. I.e. we just get id from URL. Note that for documents it won’t most probably work – you will need to open SPFile from url and get its id using SPFile.Item.ID property.

This problem is reproducible on Sharepoint 2007. I didn’t test it on Sharepoint 2010, if you will test it please share the results in comments.

How to trigger and test daily alerts in Sharepoint

In Sharepoint it is possible to create alerts with different frequency:

  • immediate – sent immediately when next time immediate alerts job will run
  • daily – sent daily also by immediate alerts job
  • weekly – sent weekly

If you create new daily alert and want to see whether it will work or not it is not very convenient to wait 24 day until Sharepoint will sent them next time. In this post I will show several ways to trigger summary alerts and send them when you need.

Method 1. When you add a new daily alert, new row is added to the SchedSubscriptions table into Sharepoint content database. This is the key element of this method. We are interesting in the following 2 columns in this table:

  1. NotifyTime
  2. NotifyTimeUNC (NotifyTime minus 3 hours)

In these columns Sharepoint stores time when next time daily alert for particular list will be sent. So first of all determine row which corresponds to your list:

   1: SELECT * FROM SchedSubscriptions

Table contains SiteUrl, WebUrl, ListUrl columns. Using them you will be able to find needed row. Copy Id (uniqueidentifier) and execute the following SQL query:

   1: declare @s datetime
   2: declare @u datetime
   3: set @s = CAST('2012-06-24 12:00:00.000' as datetime)
   4: set @u = CAST('2012-06-24 09:00:00.000' as datetime)
   5:  
   6: update dbo.SchedSubscriptions
   7: set NotifyTime = @s, NotifyTimeUTC = @u
   8: where Id = '...'

In this example in Id you should specify value which you copied from previous query’s result, @s corresponds to NotifyTime, @u to NotifyTimeUNC (NotifyTime minus 3 hours). Time should be in past (comparing with current datetime) – only in this case Sharepoint will send daily alerts.

After that wait some time. Exact time of waiting depends on the job-immediate-alerts property which can be determined by the following command:

   1: stsadm -o getproperty -pn job-immediate-alerts -url http://example.com

for testing you can set it to 1 minute:

   1: stsadm -o setproperty -pn job-immediate-alerts -url http://example.com -pv "every 1 minutes between 0 and 59"

but after testing it is better to revert it e.g. to 5 minutes:

   1: stsadm -o setproperty -pn job-immediate-alerts -url http://example.com -pv "every 5 minutes"

So after this time if you will check SchedSubscriptions table you will see that time which you updated is increased by 1 day: in out example it will be “2012-06-25 12:00:00.000” for NotifyTime and “2012-06-25 09:00:00.000” for NotifyTimeUNC. It means that Sharepoint processed daily alert and it was sent. If everything is Ok, you or alert’s recipient should get email alert with daily summary.

Method 2. I found it in the following forum thread: SPAlertHandlerParams - not behaving correctly for daily alerts, but didn’t test it by myself. May be it will be useful for you as well:

   1: SPSite site = new SPSite("http://example.com");
   2: SPWeb web = site.OpenWeb();
   3: SPAlert alert = web.Alerts[new Guid("...")];
   4: alert.AlertFrequency = SPAlertFrequency.Daily; 
   5: alert.AlertTime = DateTime.Now.AddMinutes(1);
   6: alert.Update();

For general alerts troubleshooting I recommend the following articles: The Truth About How Daily SharePoint Alerts Actually Work, Troubleshooting Alerts. They will economy some time for you. Possibility to trigger daily alerts is very important for troubleshooting. It helped me in my work, hope it will be helpful for you as well.

Friday, June 22, 2012

Fix “Invalid character” error from ScriptResource.axd

Some time ago on one of our projects customers faced with the following problem: 2 users couldn’t use functionality which was implemented with javascript. For all other users everything worked, but for these 2 users it didn’t work. We checked their browsers – they used IE8. Most of other users used IE9. The first thing we tried is to run site in IE9 with IE8 browser mode and document mode (you can change it from developer tools in IE). But it didn’t give result – we couldn’t reproduce problem locally. Then I asked to send us screenshots of the pages with broken functionality and noticed that there was javascript error icon in status bar. Customers send us description of the error:

   1: User Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0;
   2: SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729;
   3: Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E)
   4: Timestamp: Thu, 22 Jun 2012 09:00:00 UTC 
   5:  
   6: Message: Invalid character
   7: Line: 1
   8: Char: 1
   9: Code: 0
  10: URI: https://example.com/ScriptResource.axd?d=... 
  11: Message: 'Sys' is undefined
  12: Line: 148
  13: Char: 1
  14: Code: 0
  15: URI: https://example.com
  16:  
  17: Message: 'Type' is undefined
  18: Line: 2
  19: Char: 1
  20: Code: 0
  21: URI: https://example.com
  22:  
  23: Message: Object doesn't support this property or method
  24: Line: 2
  25: Char: 132
  26: Code: 0
  27: URI: https://example.com

(there were more similar errors, I copied only distinct). After brief investigation and googling I found that some time this error can be fixed by disabling script compression. It can be done by adding the following line into web.config:

   1: <system.web.extensions>
   2:   <scripting>
   3:     <scriptResourceHandler enableCompression="false" enableCaching="true" />
   4:     ...
   5:   </scripting>
   6: </system.web.extensions>

And it helped. After I added this line into web.configs on all WFEs error disappeared. Users with other browsers also still work without problems. Hope it will help you if you will encounter with similar problem.

Update 2012-06-27: another way to fix this problem is to add site to the Local Intranet zone in IE settings. After that functionality also worked without problems even after we removed scriptResourceHandler from web.config.

Saturday, June 16, 2012

Quick way to view event log from remote computer

In Sharepoint development we often have a deal with farm environments. Typical farm contains db server, app server and several WFEs. If something goes wrong one of the first thing which we make for troubleshooting is checking of Sharepoint logs and Windows event logs. Each WFE has own logs and we need to check all of them in order to be sure that don’t miss something. In order to speed up the process we may setup regular Windows share for folder with Sharepoint logs (14/Logs by default) and open it from single WFE to which we are connected by Remote desktop. But what about event log? Is there a way to quickly open it from remote computer without connecting to it via RDP? Yes it is and this way is quite simple. You need to use the following command:

eventvwr <computer name or IP address>

and it will open event log from remote computer in your RDP session. You may also connect from UI: open event log, right click top level node and select Connect to another computer:

image

Then you need to specify computer name or its IP address connect to it. More details can be found in the following technet article: Work with Event Logs on a Remote Computer.

Wednesday, June 13, 2012

Camlex.Net 3.2: add expressions to existing string queries

About half year ago we added non-trivial feature to the Camlex: reverse engineering. Details can be found here: Camlex.NET became bidirectional and goes online. Version 3.0 is released. Briefly it allows to parse existing string query into expression tree. Based on this feature we ran online service: http://camlex-online.org where you can paste string CAML query and see how it looks like with Camlex syntax.

When worked over reverse engineering, I thought that there may be many interesting applications. Today I released version 3.2 with another feature built on top of reverse engineering which is closer to applied development. Now you may pass existing string query to the Camlex and add more conditions via lambdas. I.e. you may use Camlex with tools which work with plain CAML queries, expand them using Camlex syntax and pass back to that tool.

Let’s see example. Suppose that we have a CAML query:

   1: <Where>
   2:   <Eq>
   3:     <FieldRef Name="Title" />
   4:     <Value Type="Text">test</Value>
   5:   </Eq>
   6: </Where>

it will return list items which have Title = “test”. We need to add another condition: we also want to return those items which have Title = “foo”. Now we can make it like this:

   1: string existingQuery =
   2:     "   <Where>" +
   3:     "       <Eq>" +
   4:     "           <FieldRef Name=\"Title\" />" +
   5:     "           <Value Type=\"Text\">test</Value>" +
   6:     "       </Eq>" +
   7:     "   </Where>";
   8:  
   9: var query = Camlex.Query().WhereAny(existingQuery, 
  10:     x => (string)x["Title"] == "foo").ToString();

It will produce the following result:

   1: <Where>
   2:   <Or>
   3:     <Eq>
   4:       <FieldRef Name="Title" />
   5:       <Value Type="Text">foo</Value>
   6:     </Eq>
   7:     <Eq>
   8:       <FieldRef Name="Title" />
   9:       <Value Type="Text">test</Value>
  10:     </Eq>
  11:   </Or>
  12: </Where>

By the same way you may combine conditions via WhereAll method – and in CAML you will get “And” instead of “Or”. Of course you may pass complicated queries and combine several other expressions. Camlex will parse string, create resulting expression tree and returns combined CAML:

   1: string existingQuery =
   2:     "<Where>" +
   3:     "  <And>" +
   4:     "    <Eq>" +
   5:     "      <FieldRef Name=\"Title\" />" +
   6:     "      <Value Type=\"Text\">foo</Value>" +
   7:     "    </Eq>" +
   8:     "    <Eq>" +
   9:     "      <FieldRef Name=\"Title\" />" +
  10:     "      <Value Type=\"Text\">test</Value>" +
  11:     "    </Eq>" +
  12:     "  </And>" +
  13:     "</Where>";
  14:  
  15: var query = Camlex.Query().WhereAny(existingQuery,
  16:     x => (int)x["Count"] > 1 && x["Status"] != null).ToString();

will produce:

   1: <Where>
   2:   <Or>
   3:     <And>
   4:       <Gt>
   5:         <FieldRef Name="Count" />
   6:         <Value Type="Integer">1</Value>
   7:       </Gt>
   8:       <IsNotNull>
   9:         <FieldRef Name="Status" />
  10:       </IsNotNull>
  11:     </And>
  12:     <And>
  13:       <Eq>
  14:         <FieldRef Name="Title" />
  15:         <Value Type="Text">foo</Value>
  16:       </Eq>
  17:       <Eq>
  18:         <FieldRef Name="Title" />
  19:         <Value Type="Text">test</Value>
  20:       </Eq>
  21:     </And>
  22:   </Or>
  23: </Where>

By the same way you may expand OrderBy, GroupBy and ViewFields fields:

   1: string existingQuery =
   2:     "  <OrderBy>" +
   3:     "    <FieldRef Name=\"Modified\" Ascending=\"False\" />" +
   4:     "  </OrderBy>";
   5:  
   6: var query = Camlex.Query().OrderBy(existingQuery,
   7:     x => new[]{x["Title"], x["State"] as Camlex.Asc}).ToString();

it will get the following CAML:

   1: <OrderBy>
   2:   <FieldRef Name="Modified" Ascending="False" />
   3:   <FieldRef Name="Title" />
   4:   <FieldRef Name="State" Ascending="True" />
   5: </OrderBy>

This feature came from community and we also glad to hear your proposals on codeplex site. Hope new feature will be useful and will simplify your work.

Saturday, June 9, 2012

DDD in Sharepoint. Part1– philosophical

I wanted to write a serious of posts about using of DDD in Sharepoint from a long time ago, but because of lacking of time and very tight schedule during last months can do it only now. I used DDD before to start working with Sharepoint – in ASP.Net and win forms applications. Currently I also have wide Sharepoint development and architecture design experience, including using of DDD in large Sharepoint projects. But of course I worked not only with DDD, so I can compare various approaches using my own real-life experience. Hope this reading will be useful and interesting for you.

This material will be useful mostly for developers who is working on medium and large projects, who understands the business-value which brings DDD. If you need quickly develop a web part which displays data from some storage, it will be overengineering most probably. But if your goal is to build solid and maintainable architecture for the system which supposed to be used for years, then it will be helpful. This part of the serious won’t contain any code examples. I will try to answer why and when you should use DDD. In the next parts, I will describe key technical solutions which can be used in practice. Let’s start with brief introduction to DDD and where we should use it and where not.

In Sharepoint you may manipulate data by several ways: you may work with SPListItems, OTB Content by query web part, Data view web part, BCS, external content types, etc. The advantage of this approach is that all of these comes out of the box and you may start using it very fast. But this approach has significant disadvantage: it hides business domain model from developers. SPListItem may represent OrderItem, Product, Checkout transaction and so on, it can represent anything. When you will read code which operates by list items you won’t immediately see what it does in business domain. Also code won’t reflect ubiquitous language: common language for all team members including developers and business people. As result code became less clear, it is harder to understand it, which in turn leads to more bugs and confusions. It will be hard to change it in future. Business rules are not static: in modern world business should be able to change very quickly for versatile market conditions. If your software won’t reflect business needs and won’t be ready for changes, customers will choose another vendor (one of the projects which I worked on, was the project from another vendor. The code was written using approach described in this paragraph. But it caused so many problems, so finally business decision was made and customers changed the vendor).

Another way to work with data is to build clean business model using POCO (or at least generated by automatic tools like in Linq 2 Sql) and build infrastructure for working with them by yourself. With this approach developers will write code in terms of business domain. They will manipulate by Products, Orders, Overdraw limits, etc, etc. – e.g. by terms which are understandable by business people. Yes it requires more work from you on initial phase, because you need to add extra layer on top of raw SPQuery and SPListItemCollection. Instead of using OTB CQWP you will need to create custom web part which uses repository in order to retrieve objects from storage, then convert them into view model (this step is optional) and pass to the view, which will render it for users in appropriate form. This approach follows DDD style. Although in Sharepoint you may need to combine DDD infrastructure with OTB web parts – it will allow you to decrease implementation time which may be crucial for business. This approach also works. You just need to remember that core of your application anyway should be expressed via clear domain model.

Whether you will use DDD or not I highly recommend to read Domain-Driven Design by Eric Evans or at least Domain Driven Design Quickly, if you don’t have time to read Evan’s book. I always recommend it for all developers, regardless of the technology stack or programming languages they use. Yes, I don’t ask members of my Sharepoint team to read books about Sharepoint workflows, search, BCS, web parts and other technical stuff in their free time. In most cases in order to start using them and bring value for business immediately it is enough to have some background and turn on your head (e.g. I get this background by reading blogs of developers – the same developers as I am). It is impossible to know everything, that’s why instead of reading such books, it is better to teach yourself to quickly teach any technology, but it is too philosophical and too far from current article’s subject :). Probably I will write something about it in future.

Of course there is no direct answer where to use 1st approach and where DDD-style approach. You still have to evaluate all pros and cons and make decision by yourself and take this responsibility. As any other paradigm, framework, technology – it should help you, not prevent from making things efficiently. If for supporting of DDD infrastructure you need more time than for implementing actual business tasks, probably you should re-consider it.

Personally I prefer DDD approach for most of my projects: but I work mostly with medium and large projects (starting from 2-3 man years). 4 last projects where we used DDD were successful: they were implemented in time without major problems and within budget (almost :) ). 1 large project which we got from another vendor tried to use DDD but only in one part of the functionality (say 25% of whole functionality): this project was not very successful. The reason is not only in DDD of course (main reason of success is great team and each member’s attitude, because as said Jim McCarthy in his book Software for Your Head: team = product), but from my point of view it was important success factor. I added exact digits here in order to give you ability to reference me when you will argument using of DDD for your chief :). Probably it will add some weight to your scalepan.

Another reason is more psychological: you will make your work better if you enjoy it. I enjoy working with clean model without infrastructural noise, and I use it not only in Sharepoint of course. I use it in ASP.Net MVC, web forms (yes, yes), win forms, python projects. People reading this code said that they really like it and will use it in their own projects (to be honest I heard only one negative response from the apologist of single static class on thousand lines of code. But as he was not from my team I didn’t try to argue with him. I have more useful things to do). From other side when I need to work with code for maintenance from another company where I see SPListItems, TaxonomyFieldValues, SPQuery, etc. applied without business model layer, I fill very uncomfortable. Also it makes me think why customers stopped working with vendor who created this code.

This was brief philosophical introduction to the DDD in Sharepoint. Hope it was not very boring. Promise that next parts will be more practical and will have a lot of code examples.