Monday, 30 September 2013

CRM - Lookup field name vs relationship field name

Today I thought I'd discuss a little gotcha in CRM 2011 that I'm amazed remained as a "feature" for so long! Have you ever seen "foreign key" fields in a CRM system and noticed that some are called "new_ContactId" where as others are called "new_Contact"? There's a fairly simple reason this occurs, it's basically a result of the developer/consultants preference when creating fields. Let's take a quick look at each method.

Old School - Creating relationships

If, like me, you have progressed from CRM 4 or earlier you generally create relationships between entities using the same method as always existed. You open up the entity customizations, navigate to 1:N or N:1 relationships and you get this screen:

You will notice I have highlighted what it will create the field as in the background: "new_ContactId". Most people comfortable with CRM throughout many versions will be familiar with this convention of creating fields.

New School - Lookups

Since CRM 2011 we now have a second way to create relationships. Rather than going via the relationship links on an entity simply pick the entity where you want the "foreign key" field to reside. In our example this is on the entity called "Custom Method". Navigate to the list of fields and simply add a new lookup field. Like this:

This time you'll notice I have highlighted the field name "new_Contact". We're missing the "Id" part!

Old School or New School?

I guess this is the question, which do you prefer? Personally, I prefer the new field naming convention for one simple reason. As CRM developers you don't really have your head in tables and SQL as much any more. Instead you're using plugins and Entities / Entity References. So generally in a plugin you'll use code like this:

var contactReference = (EntityReference)customMethod["new_ContactId"];

This "Id" always bugged me, because of this simple fact - it's not an Id in code, it's a reference. So if you just wanted the "Id" in code you use this:

var contactId = ((EntityReference)customMethod["new_ContactId"]).Id;

Which I don't like, because you're repeating the abbreviation "Id". In my opinion this kind of violates "DRY" so I will avoid it if at all possible. Using the new school way it looks cleaner without this repetition:

var contactId = ((EntityReference)customMethod["new_Contact"]).Id;

So whenever I create a field using the relationship convention I will manually remove the "Id" part.

Friday, 13 September 2013

What security groups do you belong to within Active Directory?

If you want to see what security groups you belong to within Active Directory open up a command prompt and run the following:

gpresult /V

This is really useful if you are having problems installing a piece of software (such as MS Dynamics CRM) and are hitting weird security problems. Quite often when installing Dynamics CRM you will have asked for and believe you have domain administrator rights, but the installer keeps telling you something like:

Action Microsoft.Crm.Setup.Server.GrantAspNetServiceAccountAccessAction failed. ---> System.Runtime.InteropServices.COMException: The security ID structure is invalid

Running the above command will give you a nice list of exactly what you have:

The user is a part of the following security groups
    Domain Users
    Remote Desktop Users
    NT AUTHORITY\Authenticated Users

Friday, 6 September 2013

Executing stand alone C# code within CRM

A question that I was asked recently, and have pondered for quite some time, is providing an ability to execute some independent/standalone C# code within a CRM instance. There are quite a few ways to do this, but I'll describe my preferred pattern here. The solution I put forward is an attempt to provide complex code solutions that do not require the use of another server or external application. And it should also work both Online and OnPremise.

Firstly, to support our "C#" calls we need a new entity that will trigger it. Let's call it "Custom Method". In it's most basic form it would be a very simple object with just 3 main attributes, the method name, parameters and result:

Next, what we would do is attach a C# plugin on the PreCreate of this that will allow us to run some C# code in the background. Using the developer tools a very basic plugin skeleton that has 1 custom method (contained within the plugin class for simplicity) will look something like this:

public class ExecuteCustomMethod: Plugin
    private readonly IDictionary<string, Func<string,string>> methods = 
     new Dictionary<string, Func<string,string>>();

    public ExecuteCustomMethod()
        : base(typeof(ExecuteCustomMethod))
        methods.Add("Add2Numbers", Add2Numbers);
        RegisteredEvents.Add(new Tuple<int, string, string, 

    protected void Execute(LocalPluginContext localContext)
        if (localContext == null)
            throw new ArgumentNullException("localContext");
        var target = (Entity)localContext

            var name = (string) target["xrm_name"];
            var parameters = (target.Contains("xrm_parameters") ? 
                              (string) target["xrm_parameters"] : 
            target["xrm_result"] = methods[name].Invoke(parameters);
        catch (Exception ex)
            target["xrm_result"] = ex.ToString();

    private string Add2Numbers(string numbers)
        var numberArray = numbers.Split('|');

        return (decimal.Parse(numberArray[0]) + 

You could easily extend the parameters and/or result to use XML and serialize/de-serialize the results to provide a more robust/complete solution. For simplicity purposes I presume the parameter will be provided in a pipe delimited format. Now, you simply execute and retrieve the results as follows:

Effectively this may offer an easy solution to execute some complex stand alone C# for whatever reason required.

Friday, 5 July 2013

CRM 2011 - Why is my Ribbon Button is disabled!

This is one of those problems that I regularly come across and it catches me out over and over again. Yesterday, yet again, I was scratching my head at yet another disabled ribbon button and thinking "why oh why didn't I document how I fixed this last time!".

The funniest part is 9 times out of 10 the solution is ridiculously straight forward. Just republish your solution! For some reason I have not yet quite figured out, maybe it's down to something funny happening in the cache, but a simple republish gives CRM the appropriate kick up the bottom it needs to get that button enabled.

Afterthought - if you're a developer, and haven't yet done so, download and use Ribbon Workbench here. Quite simply, it's just pure awesome.

Friday, 12 April 2013

MSCRM and the dreaded rsProcessingAborted error

There are several reasons why this error can happen most of which are quite easy to solve. The key to solving it is knowing how to analyse it properly.

The most common reasons I've come across for getting this problem are as follows:

  • A problem within the report or a sub report. 
  • Problems with Data Sources set on the reports
  • Reporting Extensions not installed on the report server
  • SSRS Logon context not having adequate rights
  • CRM App Pool running under a user context and not configured correctly

Generally, the issue you'll face is initially not having an awful lot of information to go on. When you run the report all you get back is:

rsProcessingAborted - An error has occurred during report processing

So, how exactly would you investigate this to get to the root cause? First, a quick checklist:

  • Open up Reporting Services: http://[SSRS Server Name]/Reports
  • Are the Data Sources set correctly? The report should be point to:
    • FetchXml reports: /[ORGNAME]_MSCRM/CustomReports/MSCRM_FetchDataSource
    • SQL reports: /[ORGNAME]_MSCRM/CustomReports/MSCRM_DataSource
  • If you open up Deployment Manager does it give you a warning about reporting extensions not being installed? If so you will need to install/re-install reporting extensions

After verifying the above and if the report still isn't working you need to do a bit more in depth analysis to get to the root of a problem like this.

Get coffee

Yes, you could be working on this for a while...

Run the report in Report Services

If nothing worked so far, check if it will run in reporting services. As I mentioned above, the URL to get to your reporting services is:

http://[SSRS Server Name]/Reports

Within here your custom reports will live in a folder like the following:

[Organization Name]_MSCRM/CustomReports

Open up the report you're having a problem with. 

Tip: You should get a page asking for your user name and password if the report is set up correctly, and you will need to enter in the following:
Username: Your systemuserid from the systemuser table for your user record.
Password: Your organizationid from the organization table within MSCRM_Config

If the report doesn't work then it might give you more information about the problem that will lead to the solution. Quite commonly you might see an error such as "Cannot create a connection to Data Source XYZ" or something like this. Or maybe it will be something syntactically wrong with the report and not related to the Data Source. If so it's problem with the report itself that you will have to fix.

If it does run, and you've confirmed the above check list, then the only questions I can think of is:

1. Have you been using shared datasets? If so then the bad news is I don't think these will work with Dynamics CRM. Although, I haven't confirmed if shared datasets can be used with Dynamics CRM so if anyone can confirm this please let me know. But generally, if I see a shared dataset I immediately convert it to an embedded dataset and link it to a shared data source instead. If you've edited it using Report Builder you should be able to choose the MSCRM_DataSource for SQL or MSCRM_FetchDataSource for FetchXml. If you're using visual studio you can create an embedded datasource and then convert it to shared afterwards.

2. Have you been using a child/parent folder structure for parent/sub reports? If so, I don't think you can easily do this in CRM either. I'm sure it's possible, but it's probably tedious to get working. So you'll need to make sure they're all in the same folder and correctly linking to each other.

Check Reporting Services logs

If all is still unwell, you will need to check the reporting services logs. You can also check Event Logs on the SSRS server, but the Reporting Server logs will give you much more detail. These will be located on the SSRS server at a location similar to the following:

C:\Program Files\Microsoft SQL Server\MSRS11.MSSQLSERVER\Reporting Services\LogFiles

But, before we open that file let's go back to the report in CRM and generate the rsProcessingAborted error again. Once you have go back to the SSRS server and open the latest log file in that folder. There are many types of errors you will see, some of which I will attempt to deal with here:

SSRS Account Rights

If you see an error like this:
e ERROR: Throwing Microsoft.ReportingServices.ReportProcessing.ReportProcessingException: , Microsoft.ReportingServices.ReportProcessing.ReportProcessingException: Cannot create a connection to data source 'CRM'. ---> Microsoft.Crm.Reporting.DataExtensionShim.Common.ReportExecutionException: Immediate caller DOMAIN\SSRS_Account has insufficient privilege to run report as user S-[XYZ]

This generally means you are running the SSRS service under a domain account (as opposed to NetworkService or similar) and it doesn't have adequate rights. In the above example the user "SSRS_Account" was our domain account and it needs to be added to the following groups:

[DOMAIN]\PrivReportingGroup {GUID}
[DOMAIN]\ReportingGroup {GUID}
[DOMAIN]\PrivUserGroup {GUID}

These must match the same groups that exist in the security section of the [ORGNAME]_MSCRM database. For example:

Note: You should not add the SSRS_Account to the group SQLAccessGroup. If you do you may encounter errors installing / reinstalling Reporting Extensions such as this:

A Microsoft Dynamics CRM Server component is using the same account as the instance of SQL Server Reporting Services

Note: As mentioned by Nathan in the comments, he did not have the ability to modify the AD groups of this user. So he managed to solve the problem by accessing the 'Reporting Services Configuration Manager' on the Reporting Server and under the 'Exectution Account' tab he simply unchecked the box here. This meant the report would authenticate through the CRM credentials (which did have access) and not the SSRS account. Worth a try if you have the same issue.

SPN / SSPI issues

If you see an error like this:

Microsoft.ReportingServices.ReportProcessing.ReportProcessingException: Query execution failed for dataset 'DSMain'. ---> Microsoft.Crm.Reporting.DataExtensionShim.Common.ReportExecutionException:
Microsoft.Crm.CrmException: An unexpected error occurred.
System.ServiceModel.Security.SecurityNegotiationException: A call to SSPI failed, see inner exception.
System.Security.Authentication.AuthenticationException: A call to SSPI failed, see inner exception.
System.ComponentModel.Win32Exception: The target principal name is incorrect ---> 

For this error 9 times out of 10 you're running the CRM app pool in IIS under the context of a domain user. To fix this problem you'll have to be a little careful. You should NOT attempt the following during working hours or on a live and active system. Basically what you need is a few SPN records and a bit of trust to help the user context hit the servers correctly. For the sake of this post let's say the user account used on the IIS Application Pool is CRMAPP_Account and SQL Server runs under the context of SQL_Account.

Firstly, you'll need to ensure the account "CRMAPP_Account" has the option to "trust this user for delegation to any service (Kerberos only)":

Next, log onto the CRM Appliction server and run the following commands:

setspn  -a  HTTP/[Crm App Server Name]  [Domain]\CRMAPP_Account
setspn  -a  HTTP/[Crm App Server FQDN]  [Domain]\CRMAPP_Account

The Crm App Server FQDN might be something like or something slightly different, so you'll need to figure this out for your specific environment. For example, if your server name is CrmAppServer and your domain is FooDomain.local you will have something like the following:

setspn  -a  HTTP/CrmAppServer  FooDomain\CRMAPP_Account
setspn  -a  HTTP/CrmAppServer.FooDomain.local  FooDomain\CRMAPP_Account

Next, if SQL also runs under a domain account we'll need to log onto the SQL server and run similar commands for the SQL account:

setspn  -a  MSSQLSvc/[SQL Server Name]:1433 [Domain]\SQL_Account
setspn  -a  MSSQLSvc/[Sql Server FQDN]:1433 [Domain]\SQL_Account

Again, the SQL Server FQDN will be the fully qualified domain name of your SQL Server. So you might have something like this: Once you have figured this out, run the above commands and it will add all the required SPNs to get rid of the problem.

9 times out of 10 this should be all you need to do...

No wait! That broke my CRM authentication?!?

And this is why we need to do this outside of working hours! After doing all of this, and as pointed out by James in the comments (thank you!), you may need to enable the site to use the app pool credentials.

If IIS has not been set up for windows authentication you may still notice a problem every time you try access CRM outside of the App Server. You'll be repeatedly prompted for your user details and eventually will see an error like "No authentication mode available" or something similar to this. Firstly, I've read mixed reports on whether you need to enable or disable Kernal-mode authentication, but I have found that it needs to be enabled. The steps do do this are
  • On the Microsoft Dynamics CRM server(s), open up IIS Manager:
    • Start > Run: inetmgr
  • Expand SERVER > Sites
  • Click on Microsoft Dynamics CRM 
  • Within the Features view, double click on Authentication
  • Right-click on Windows Authentication and go to Advanced Settings
  • Check "Enable Kernel-mode authentication"
  • Click Ok

Here's an MS article describing a similar issue with web resources

Another problem I have found on some servers is the order of providers can cause problems. If the above doesn't fix the authentication then check the following out too:

  • Navigate to Authentication again as per the previous steps
  • Right-click on Windows Authentication and go to Providers
  • Ensure Negotiate is the first provider and NTLM is the second
  • Click Ok


That covers a lot of the problems I have run into. I know there's more out there that I haven't, but hopefully these are the most common that people run into. If you have run into some kind of Reporting Services issue I haven't dealt with here don't be afraid to ask and I'll help if I can!

Monday, 18 March 2013

Some of the things I love and hate about Silverlight

After a previous job I had decided Silverlight wasn't the greatest tool in the world and vowed to push HTML5 as a better solution for MS CRM in the future. But, in my current position we use Silverlight quite a lot so I ended up back developing some pages in it again. I constantly ask the question why people continue to develop on a framework that will be eventually unsupported? But I guess the answer is people learnt to love the beast and found it hard to move on! And I have to admit, one of the beautiful things about Silverlight is a lot of those niceties you get from WPF also exist in Silverlight, such as slick interfaces and speedy UI development.

Unfortunately that probably just about completes the list of things I love about silverlight. Because with its slightly different and vastly reduced framework it also brings a lot of nuisances. These are noticeable in the controls, libraries (or lack thereof) and general functionality. Here are a few of the most recent/common issues I have come across:

Text boxes

There are a few problems with text boxes, but only one I shall mention here. Ever try setting up an autoselect text in Silverlight? Yes, you'll notice you don't have an option or property to do this. This to me is a massive gaping hole, because it's one of the very first things I write or implement every time I write a new Silverlight page with text fields. A really neat way around this is to create new behaviour for yourself and enable it via a property on the Silverlight page. Effectively all this behaviour needs to do is capture the focus event in the background and select all text upon entry

Combo boxes.

There are several annoying issues with Silverlight combo boxes. Firstly, have you ever tried to use a combo box in a really height restricted Silverlight page? It's just not at all viable and is something I have hit a few times in MS Dynamics CRM. If you need to develop a small web resource for a form that will contain a combo box you'll find it won't work, because your "frame" or area of use is restricted to the size of the Silverlight control, a bit like flash I guess. So even if your page is huge this is the sort of behaviour you'll notice happening on a combo box similar to the "Cars" combo box on my form:

The next problem with combo boxes that is quite annoying is if you add a blank row to a combo box it doesn't display it with the correct height:

See how we just have a little slither of a line above "Ford"? Well that's the blank row. This is incredibly annoying, but thankfully there's a quick but slightly dirty way to get around this, instead of using an empty string add a space to the string and suddenly it display it correctly.

Another nuisance of combo boxes is the direction the item list renders. Because of the first problem with Silverlight not being able to use the space outside of the control's frame it needs to be clever in which direction, up or down, the item list is rendered. This behaviour works fine first time around, as in the item list will render upwards if there is more room in that direction. But after that first time you've used the combo box it all goes a bit pear shaped:

(Yes, I notice "Aston" has a fantastic spelling fail... but I really can't be bothered to update the screen shot!)

There are a few ways to fix this, but the 2 best solutions, in my opinion, are

  1. Instead of binding to custom objects create ComboBoxItems and bind your combo box to a collection of those instead. (The Silverlight bug only rears its head when bound to custom objects)
  2. Create your own combo box template with the correct behaviour (can't believe I just wrote that!)
There are more combo box nuisances but these for me are the worst. Apart from the first one, which is more of a limitation, those second 2 are bugs that should never have made it to the public community.

Silverlight, JSON and dynamic types

The next thing I hate about Silverlight is the choice of deprecated libraries. I understand that they had to reduce the framework to make it somewhat viable for browser use, but why deprecate a library that's so closely linked to browsers and javascript? In short, if you want to parse JSON into a dynamic type in Silverlight you have to write your own parser. For more information see my blog post on the matter: silverlight-json-and-dynamic-types.

Assembly Library Caching

I don't have an issue with this actual concept, more of how it was implemented. It's quite common that you'll share XAPs between Silverlight pages and controls, such as images for example. So to reduce XAP sizes there is no real point including these in every single XAP file. If we split them up we can make use of caching and speed up our overall download times. The issue here is how this works in conjunction with MS Dynamics CRM (I haven't chosen which framework I will blame yet...). When you enable this in the project properties it splits up the relevant files into ZIP files (not XAP files). This is a royal pain in the buttocks, because you cannot upload ZIP files to MS Dynamics CRM. I have never bothered getting around this, but I'm guessing you could unzip the main XAP file, open up the AppManifest file, change the references to XAP instead of ZIP, and repack the XAP file again. Also you'd need to ensure you rename your other resources to XAP instead of ZIP. Unfortunately you would have to do this after every build, so you might have to write a post build script rather than performing it as a manual task. I haven't tested this yet either, but I'm fairly sure it would work fine once put in place.

Exposing browser error messages

Ever get that infamous error "The remote server returned an error: NotFound"? This is basically Silverlight telling you "something screwed up, but I have no idea what it was". To get your hands on the error messages you need to perform some registering on application start up:

WebRequest.RegisterPrefix("http://", System.Net.Browser.WebRequestCreator.ClientHttp)

This will get you better error messages, but has a massive drawback. If you're making a lot of simultaneous asynchronous calls your browser will now frequently crash. I'm not sure what it's doing behind the scenes here, but neither Silverlight or your browser seem to like it. 99% of the time I find myself removing that line and accepting rubbish error messages.

I could go on...

... but I won't. I'm sure not the first person who's come up with a list like this and I'm guessing I won't be the last.


So, my conclusion out of all of this is let's all admit that Silverlight was a nicety at the time, and we all enjoyed it to begin with. But in reality, HTML wins yet again, so can we please just let it die with dignity. Yes, it's easier to throw together a UI in Silverlight, but once you start binding that baby together, setting up all of those models and viewmodels and have gone through about 4 iterations of async callback hell and rewriting missing feature/property helpers, you'd have written 80% / 90% of the exact same thing in HTML5 and JQuery. Also, what with Silverlight not seeing any more upgrades after 5 I envisage a framework that will retain these bugs for some time yet. So for me it's time to drop the Silverlight baton (yet again!) and push HTML5 where ever I go.

Tuesday, 12 March 2013

Silverlight - Passing javascript objects into a web page dialog arguments

I asked this question on Stack Overflow a while back and was a little surprised that it earned me the tumbleweed award. Has nobody seriously ever done this? Here's the original question: silverlight-passing-an-array-to-a-web-pages-dialog-arguments. It's a scenario quite a few Microsoft Dynamics CRM developers might come across if trying to invoke MSCRM web dialogs from silverlight. But for the purpose of this post I'll keep it somewhat generic.

Take this scenario, you have a web page dialog that requires some dialog arguments to work correctly. And it performs the following when loaded up:

var args = getDialogArguments();
if (args == null) return;
if (args.items == null) return;
var items = args.items;

var len = items.length;
for (var i = 0; i < len; i++)
  var item = items[i]; = item.getAttribute("oid");
  cur.type = item.getAttribute("otype");
  cur.values = item.values;
  ... etc

We want to invoke this page via Silverlight, but the question is how do we pass in the arguments correctly? If we take a closer look at the dialog arguments (args) we can see that it has a member called "items" which is an array. Each of these items have attributes called "oid" and "otype". So let me explain what you need to do to set this up.

Before I continue, I want to add a "rule" before I explain how to achieve this. We need to do this without using "dynamic" because this causes you to have to reference the Microsoft.CSharp library which in turn causes your XAP file to bloat.

To start let's ask a slightly different question, what do these objects materialise themselves as within Silverlight? This I already knew the answer to, they are of type ScriptObject which is located in System.Windows.Browser. So why can't we just go and create one of these? Here is where I hit my first roadblock, it has an internal constructor. But, a quick search across the internet reveals that we can set this up using the following:

var dialogArgs = HtmlPage.Window.CreateInstance("Object");

And how about a property on this field?

dialogArgs.SetProperty("items", items);

Excellent, so now we're getting somewhere. Next up, how do you set up this array called "items"? Same way, but we can add indexers to it. Some code for setting up an array and an item will look something like this (I have just created a new GUID for the purpose of this example):

var item = HtmlPage.Window.CreateInstance("Object");
item.SetProperty("oid", Guid.NewGuid());
item.SetProperty("otype", "account");
var items = HtmlPage.Window.CreateInstance("Object");
items.SetProperty(0, item);

And finally, just pass that object straight into your dialog window like this:

var so = (ScriptObject)HtmlPage.Window.Invoke("showModalDialog", lookUpWindow, dialogArgs, "dialogWidth:600px;dialogHeight:600px;");

Job done.

Wednesday, 6 March 2013

Always check your sources before believing a blog post...

With the explosion of information and blogs on the internet the importance of knowing your sources and, more importantly, checking out the facts has become so crucial in modern times. When people say "don't believe everything you hear/read" that could not be any closer to the truth. Even if the sources look like they come from an honorable source tread warily. Always check your facts in more than 1 place!

I fell into this trap quite recently when it came to writing a workflow in MS Dynamics. Now, let me tell you, I have been a MS Dynamics Developer/Architect/Consultant for a good few years now so consider myself close to expert in a lot of areas in this field. But, would you believe, one thing that I never had the pleasure of doing up until recently was using wait conditions and timeouts within a workflow. My scenario was thus:
User creates a quote
Quote is linked to an "event" which has an "event start date"
If user doesn't proceed with the Quote: leave open until the event has passed
When the event passes, the quote/opportunity is closed as lost.
Ok, easy workflow I hear you say. And so it should be. I browse online to find the best way to do this and come across a blog post with the following in the title "Workflow Wait Conditions: Best Practices - Dynamics CRM 2011". This is my gold! It's on so it cannot be wrong! Right? ... Nope, unfortunately it was flawed! Here's what caught me - from using the advice on that post I decided a timeout was a bad idea, so attempted to use a wait condition instead. And a direct quote from the blog was this:
We can achieve most of the timeout functionality by simply using the process execution time. Since the process execution time is always the current time, we can construct a wait condition that...
This statement is incorrect, but I was not to know this yet. So I proceeded to create a wait condition for the following:
Wait Until Process Execution Time > Event Start Date.
And you guessed it, my process never completed. Why? The process execution time is actually the start time OR the resume time, so it never changes unless you stop and start your workflow. Therefore it is not always the current date/time. A quick search of the internets and I find a different blog quoting the exact opposite.

So folks, moral of the story, don't believe everything you read out there. Always, always, ALWAYS double check your sources!

 Happy coding.

Tuesday, 19 February 2013

CRM 2011 - How do you close an opportunity in Silverlight?

This is an interesting one I came across today. Due to using a different set of objects within the Silverlight framework you'll notice you're missing that valuable "WinOpportunityRequest" object! So, it begs the question, how do you close an opportunity in Silverlight?

Thankfully, we can go back to basics and raise an ordinary OrganizationRequest to achieve this. But before we do this let's take a look at what the code might look like in a plugin or standard CRM service request:

var opportunityClose = new Entity("opportunityclose");
    new EntityReference("opportunity", opportunityId));
opportunityClose.Attributes.Add("subject", "Opportunity expired");

var winOpportunity = new WinOpportunityRequest
        OpportunityClose = opportunityClose,
        Status = new OptionSetValue(20001) // or whatever is valid for you

You might be tempted to try achieve this using a SetState request, but you'll quickly find CRM complaining and telling you you're not allowed to do it. Instead, an easier way to do this is just replicate the above call as if you were using late binding. Like this:

var organizationRequest = new OrganizationRequest
   { RequestName = (won ? "WinOpportunity" : "LoseOpportunity") };
var opportunityClose = new Entity {LogicalName = "opportunityclose"};
SetAttribute(opportunityClose, "opportunityid", opportunityEntity.Id);
SetAttribute(opportunityClose, "subject", "Opportunity Expired");
organizationRequest["OpportunityClose"] = opportunityClose;
organizationRequest["Status"] = new OptionSetValue {Value = 20001}; // or whatever is valid for you

For reference, my SetAttribute function just looks like this:

private void SetAttribute(Entity entity, string attribute, object value)
  if (entity != null)
    if (entity.Attributes == null) entity.Attributes = new AttributeCollection();
    if (entity.Attributes.ContainsKey(attribute))
       entity.Attributes.SetItem(attribute, value);
          new Common.XrmSoapService.KeyValuePair<string, object> 
             { Key = attribute, Value = value });