Running Microsoft CRM 3.0 under .NET 2.0

I’ve been told and have read many times that it’s not possible to run Microsoft CRM 3.0 under .NET 2.0.  Well, I guess ‘not possible’ could just mean ‘not supported’.  All of our code base is now .NET 2.0 and I was loathed to maintain .NET 1.1 versions of our framework libraries jsut for a couple of calls out of CRM. 


I had previously found this article on extending CRM with ASP.NET 2.0 apps, but needed to create a Workflow component to call into our internal systems and found that this obviously didn’t work because (all of) CRM runs under .NET 1.1.


A bit of ‘give it a try’ later and I can reveal that it is indeed possible to successfully run CRM 3.0 in a .NET 2.0 process, and also use .NET 2.0 assemblies with Workflow.  I’ll leave it up to the reader to decide whether this is a good idea that they’d actually want to implement!  The process isn’t too onerous so wouldn’t take long to roll back if you encountered problems.


If you’re still up for giving it a try then you’ll need to alter the ASP.NET properties for the CRM Web Site and Service …


Make sure you’ve set up separate app pools (see below), as one will run all your ASP.NET 1.1 apps, and the other will run the .NET 2.0 apps.  You can’t run a mixture of runtimes within the same app pool.


Change the Microsoft CRM v3.0 site’s root folder, AND the MSCRMServices virtual folder as below to use .NET 2.0 (and make sure they’re configured to be in your .NET 2.0 app pool).



Now to ensure Workflow can use .NET 2.0 you’ll need to add a .NET config file to force the Workflow Service to run in the .NET 2.0 runtime.


Stop the “Microsoft CRM Workflow Service”, then Add a file as below in the CRM Workflow binary folder.



The contents of the file should be as follows:


<configuration>
  <startup>
    <supportedRuntime version=”v2.0.50727″ />
  </startup>
</configuration>


Restart the Workflow Service (and probably do another IISReset for good measure), and you should find everything comes up fine, and you’ll be able to configure Workflow as per normal but now with .NET 2.0 assemblies.  I’m not too familiar with ‘where’ callouts run, but I’d presume that the same principles should apply and .NET 2 assemblies should work OK.


This procedure is obviously not supported by Microsoft so you use at your own risk, but I’ve had no problems so far….


 


 

Microsoft CRM – Data Migration Framework (DMF) Bugs / Fixes

I’ve been migrating data for a client recently and part of this has required the use of the MS Data Migration Framework (DMF).  I thought I’d post a couple of bugs I found and(thankfully) how I managed to get past them with the use of SQL Profiler!


The Migration Framework works – albeit slowly as it uses the CRM Web Service to stuff the records into the CRM database.  The CRM structure I’ve been trying to migrate to contains a large number of custom fields.  The DMF calls many stored procs to get its data (from the CDF database); these are structured by entity e.g


p_dm_GetContact
p_dm_GetContract


I was having a problem with Contacts, and after tracing a migration attempt with cut-down data (for speed), I found that the issue was that the lid was being blown off a VARCHAR(8000) in p_dm_GetContact giving the following SQL Error:


Error: 207, Severity: 16, State: 3


The error is ‘Invalid Column Name’.  Further investigation revealed that a slightly dodgy piece of code (replicated in all the other similar procs) effectively truncates the dynamically created field list in the ‘SELECT’ statement…


— if there are extended fields, then attached them to the end 
—   of the base field list

if len(@metadatafields) > 0
    set @entityfields = @entityfields + ‘, ‘ + rtrim(@metadatafields)


@entityfields contains a comma delimited list of fields from the ‘cdf_Contact’ table, and @metadatafields contains fields from ‘cdf_Contact_Ext’ (custom fields).  If you concatenate the two you’re obviously asking for trouble.


The following shows the ‘fix’ to enable a bit more room (it’s still not very good – but as the code’s not available we can’t make a nicer fix right now)


CREATE procedure p_dm_GetContact
as
begin
    set nocount on
    
    declare 
@entity            varchar(100
)
    
declare @entityfields     varchar(8000
)
    
declare @metadatafields varchar(8000
)
    
— set target entity name
    
set @entity 
‘Contact’

    
— query base field list for targeted entity
    
exec p_dm_GetFields @entity, ‘c’, @entityfields 
output

    
— query extended field list for targeted entity
    
exec p_dm_GetMetaDataFields @entity, ‘ext’, @metadatafields 
output

    
— if there are fields, then attached them to the end 
        —   of the base field list    
    
if len(@entityfields) > 
0
       
set @entityfields ‘, ‘ rtrim
(@entityfields)

    
— if there are extended fields, then attached them to the end 
        —   of the base field list
——    if len(@metadatafields) > 0
——        set @entityfields = @entityfields + ‘, ‘ + rtrim(@metadatafields)

    
if len(@metadatafields) > 0
        
set @metadatafields ‘, ‘ rtrim
(@metadatafields)

    
— build/execute query statement
    — the status code must be null
    
exec (
‘    select top 1 ”” As CRMUpdateStatus, 
            c.ContactId As entityid,
            c.StateCode As statecode,
            dbo.p_fn_GetID(l.DestinationId) As originatingleadid, 
            dbo.p_fn_GetID(p.DestinationId) As defaultpriceLevelid, 
            dbo.p_fn_GetID(a.DestinationId) As accountid, 
            dbo.p_fn_GetID(su.DestinationId) As owninguser 
            ‘ 
+ @entityfields + @metadatafields + 


                 from cdf_Contact c

       inner join cdf_Contact_info info 
                   on c.ContactId = info.ContactId

       left outer join cdf_Contact_ext ext 
                   on c.ContactId = ext.ContactId

      left outer join cdf_Lead_info l 
                   on c.OriginatingLeadId = l.LeadId

      left outer join cdf_PriceLevel_info p 
                   on c.DefaultPriceLevelId = p.PriceLevelId

      left outer join cdf_Account_info a 
                   on c.AccountId = a.AccountId

           inner join cdf_SystemUser_info su 
                   on c.OwningUser = su.SystemUserId

           where (info.StatusCode is null)
         and (info.DestinationId is null)’
)
end
GO



Colorized by: CarlosAg.CodeColorizer