Technical blog for Paul Devenney, highlighting some of the more interesting technologies, techniques and gotchas that I come across. Mostly focusing on Microsoft .NET, MVC, Windows Azure and Orchard CMS
Monday, 23 June 2014
It's great when your tools make you more effective...
I love it when a plan comes together....
Thursday, 4 July 2013
A simple Orchard module to inject a diagnostics shape into every page of your Orchard site
- A feature in a module: to toggle on/off the ability
- A class implementing Orchards FilterProvider
- A view file to be used as a shape
First thing to do is create a new module, with a feature inside. If you need a primer, try the Orchard Walkthrough and the Hello World Module example. This article assumes that you create a module called "MyModule", and that you make a feature called "MyModule.SessionChecker"
Orchard.Mvc.Filters.FilterProvider is an abstract class that implements IDependency. This means that if your feature implements the class, it will automatically be wired up by Orchard at run time. All you need to do is fill in the methods of the class in your own inherited version, like below.[OrchardFeature("MyModule.SessionChecker")] public class SessionCheckerFilter : FilterProvider, IResultFilter { private readonly IWorkContextAccessor _workContextAccessor; private readonly IShapeFactory _shapeFactory; public SessionCheckerFilter(IWorkContextAccessor workContextAccessor, IShapeFactory shapeFactory) { _workContextAccessor = workContextAccessor; _shapeFactory = shapeFactory; } public void OnResultExecuting(ResultExecutingContext filterContext) { if (filterContext.Result as ViewResult == null) { return; } _workContextAccessor.GetContext(filterContext).Layout.Zones["Body"].Add(_shapeFactory.Create("diagnosticview"), ":before"); } public void OnResultExecuted(ResultExecutedContext filterContext) { } }
The key things we are doing here are
- Using the
OnResultExecutingmethod to ensure we hook in as the result is being formed - Creating a new shape and returning it inside the body zone of the page
IShapeFactory to create an arbitrary shape. The name of this shape is the name of a razor view that we need to create in our views folder in the module.diagnosticsview.cshtml to the "views" folder in your module. The shape factory will find and use this shape. I've made my example as follows: <style>
.SessionChecker {
position:absolute;
left:0px;
top:0px;
z-index:100;
border:solid 1pt #AAAAAA;
background-color: #EEEEEE;
padding: 5px;
font-family: consolas, arial;
font-size: 10pt;
}
.SessionChecker b {
font-weight: bold;
}
</style>
<div class="SessionChecker">
Current Value for Session["TestSessionManagement"]: @Session["TestSessionManagement"]
</div>
And all this does is show the value of a particular session setting I'm interested in, but you could make yours much more interesting. Any loigic more complex than that shown should be done within the FilterProvider class you implemented, and be passed to the view as a model
Lastly, make sure to enable your module/feature to see the results. When you don't want them appearing any more, disable the module! Excellent for temporary diagnostic scenarios. For added bonus - only show it when the current user is an admin!
Thursday, 6 June 2013
Gotcha in Orchard CMS RoutesDescriptor when using Multi-Tenancy
public class ErrorHandlingRoutesProvider : IRouteProvider { public IEnumerable<routedescriptor> GetRoutes() { return new[]{ new RouteDescriptor{ Name = "ErrorRoute", Priority = 1, Route = new Route( "Error", new RouteValueDictionary{ {"action", "ErrorPage"}, {"controller", "ErrorHandler"}, {"area", "BG.Shared.ErrorHandling"} }, new RouteValueDictionary(),//constraints (none here) new RouteValueDictionary{ {"area", "BG.Shared.ErrorHandling"} }, new MvcRouteHandler()) }; }
This works as intended in a single site. However, when you have two tenants using the same module, this will fail with an error similar to the following:
System.ArgumentException: A route named 'ErrorRoute' is already in the route collection. Route names must be unique. Parameter name: name at System.Web.Routing.RouteCollection.Add(String name, RouteBase item) at Orchard.Mvc.Routes.RoutePublisher.Publish(IEnumerable`1 routes) in d:\Workspaces\GitHub\src\Orchard\Mvc\Routes\RoutePublisher.cs:line 100 at Orchard.Environment.DefaultOrchardShell.Activate() in d:\Workspaces\GitHub\src\Orchard\Environment\DefaultOrchardShell.cs:line 48 at Orchard.Environment.DefaultOrchardHost.ActivateShell(ShellContext context) in d:\Workspaces\GitHub\src\Orchard\Environment\DefaultOrchardHost.cs:line 156 at Orchard.Environment.DefaultOrchardHost.CreateAndActivateShells() in d:\Workspaces\GitHub\src\Orchard\Environment\DefaultOrchardHost.cs:line 135
The workaround is to comment out the "Name" attribute of the RouteDescriptor, as follows
public IEnumerable<RouteDescriptor> GetRoutes() { return new[]{ new RouteDescriptor{ //Name = "ErrorRoute", //This doesn't work in Multi-Tenancy Priority = 1, Route = new Route( "Error", new RouteValueDictionary{ {"action", "ErrorPage"}, {"controller", "ErrorHandler"}, {"area", "BG.Shared.ErrorHandling"} }, new RouteValueDictionary(),//constraints (none here) new RouteValueDictionary{ {"area", "BG.Shared.ErrorHandling"} }, new MvcRouteHandler()) } }; }I've started investigating this with the Orchard team, but the workaround doesn't really appear to have any drawbacks.
Wednesday, 18 January 2012
Tuesday, 19 July 2011
Unreadable content was found in this item - PerformancePoint 2007 to 2010 Migration
The Problem:
When you run the Import PerformancePoint 2007 Content wizard, using a valid account to connect to SQL server, and a valid BI Center as the target locations (which the wizard very kindly automatically identies and selects for you), you still receive the following message in each section."Unreadable content was found in this item".
There are two possibilities for this problem:
When it happens to all sections of the import (data sources, indicators, KPIs, Report views, score cards, dashboards):
The likely reason here is that the server needs to communicate with itself using a web service URL, and the (cursed) loopback adapter check is on. For this to be solved, the server must be able to access the target web application (e.g http://myintranet.company.com) from the local machine. This is easy to test. Open your browser from the server and see if you can. The following steps will resolve this issue:
Remove the loopback adapter check using the following powershell:
# Disable the Loopback Check
#This setting usually kicks out a 401 error when you try to navigate to sites that resolve to a loopback address e.g. 127.0.0.1
New-ItemProperty HKLM:\System\CurrentControlSet\Control\Lsa -Name "DisableLoopbackCheck" -value "1" -PropertyType dword
Ensure that a host entry exists for the site, either by DNS (for already in production systems), or, if you are in testing, or don't have access to DNS, via adding the hosts entry to your hosts file at c:\windows\system32\drivers\etc\hosts, e.g.
127.0.0.1 myintranet.company.com
References: http://social.technet.microsoft.com/Forums/en/sharepoint2010setup/thread/306c59e0-c74c-4f37-9df3-2b1202cef54e, http://sptwentyten.wordpress.com/2010/03/06/disable-the-loopback-check-via-powershell/
When it only happens to data sources and scorecards
My problem continued to persist past the first problem above. On further investigation of the SharePoint ULS logs, you should see messages similar to:
Failed to look up string with key "Section2TitleResource", keyfile osrvcore. edb1db92-2bd9-4dab-b772-b3b36b293e99
Unreadable content was found in this item. System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary. at System.ThrowHelper.ThrowKeyNotFoundException() at System.Collections.Generic.Dictionary`2.get_Item(TKey key) at Microsoft.SharePoint.Administration.SPScenarioContext.RetrieveDataFromSessionState(String key) at Microsoft.PerformancePoint.ImportUtility.UI.WebPages.ScenarioPageBase.Page_Init(Object sender, EventArgs e) edb1db92-2bd9-4dab-b772-b3b36b293e99
Now, this might lead you to think that something has not installed correctly, as it is complaining about resource files, but like nearly all problems in SharePoint, it comes back to permissions, and user context. The key issue in my case? I had created my own web service application pool to run the PerformancePoint service application under. The managed account for the application pool was a low privilege account (not the farm account for example).
The key thing I had missed was, that I needed to run the following PowerShell to provide my service account with appropriate object access to run the wizard successfully.
$w = Get-SPWebApplication("http://myintranet.company.com")
$w.GrantAccessToProcessIdentity("dev\svc_PPServices")
To be fair, Microsoft do list this in technet, but as with so many MS articles, they fail to tell you why, or how to recognise when you have not done this step.
references: http://technet.microsoft.com/en-us/library/ee748643.aspx
Monday, 11 July 2011
SharePoint 2010 Capacity Guidelines updated for SP1
http://sharepoint.microsoft.com/blog/Pages/BlogPost.aspx?pID=988
Membership & BDC import status hangs with infinite items (SharePoint 2007)
- The log file for SharePoint_SSP_Search (the SSP Search database) becomes full, and cannot grow further. The database stops responding
- The Office Search Service , then the SharePoint timer service throws an error as it cannot contact the database. This causes an error when the crawler tries to pause, and the object cache becomes corrupted (slight speculation here).
- The SharePoint services continually attempt to communicate with the SQL Server (every second), the net result being that the server is too busy to serve RDP requests (not ideal if doing remote support!)
- Someone “resolves” the database log size, and restarts all the SharePoint services, including IIS, the Timer Service and Office Search (or server reset)
- Office Search and Timer resumes, persisting the corrupted object cache data to the database. This has a knock on impact on the “Membership and BDC Import”, which gets stuck in some form of infinite loop (last count, over “56,000” AD records had been imported, in an AD of less than 2,000!)
- This fills up the transaction log at the rate of a few 100MB a minute, quickly reaching the max log file size again, causing SharePoint to stop responding once more.
- The log file had been capped at 5GB. This is not necessarily unreasonable, but if it does reach the limit, this triggers the issue.
- The database is in simple recovery mode
- The log file will grow with each SharePoint content crawl, or user profile import.
- The database backup for SharePoint_SSP_Search appears to not be on a daily schedule (last backup was more than 10 days previous). This means that the log file has significantly more time to grow, and shrinking the log nightly has no impact, due to the fact that the database (despite being in simple mode recovery) has not been backed up, and the transaction log space has not been freed for deletion.
- Manually backup/shrink the search database. Note, if your database is maxed on the log file, you may need to allocate more log file space before you can succeed in a manual backup/shrink
- Created a particular maintenance plan for this database, which backs up nightly, then shrinks the log file. (this stops the trigger for the issue from occurring under "normal" conditions.
- Stop the troublesome “Membership and BDC Import”
- Reset all crawled content, which clears out the corrupted data from the search database
- Started a full user profile import and confirm success.
- Initiated a full crawl on “Local Office SharePoint Server Sites”, and confirm success