Notes from 'Working with Microsoft Dynamics™ CRM 3.0

Last updated: 2006


Entity Type Notes
Custom New entities types. Can be deleted
System (Customizable) Existing entity types that support limited customization allowed. Cannot be deleted.
System (Non-customizable) Existing entity types that cannot be customized or deleted. Cannot add form or views.

Attribute Notes
Display Name UI text
Schema Name SQL column name (aka 'metadata scheme name'). Schema name cannot be changed after creation. Default schema prefix can be set up in Settings\Organization Settings.
Note: While the schema name is lowered in CRM, it's propered in the database.
Requirement Level
  • No Constraint
    No validation. Label appears normal on forms.
  • Business Recommended
    No validation. Label appears in blue and is bolded. Validation is not enforced and no message is presented if no value is entered.
  • Business Required
    Mandatory field. Label appears in red and is bolded. Entity cannot be saved if a value hasn't been supplied.
Type SQL Data Type (nvarchar, int, ntext, datetime, bit, float, money) plus:
  • picklist
  • status
  • sate
  • primarykey
  • owner
  • lookup
Format Dependent on Type. For example, varchar supports: E-mail, Text, Text area (multiline text box), URL and Ticker symbol.
Maximum Length /
List Value /
Default Value /
Minimum Value /
Maximum Value / Precision
As they sound. Only options relevant to the selected datatype are displayed.
Description Help text for Sys Admins. Not visible to end users.

Users / Licensing

Access Level Notes
None Deny
Business Unit  
Parent : Child  
Organizational Superuser



Server Side Customizations

SDK Web Service

Service Methods

Retrieve Sample

ColumnSet columnSet = new ColumnSet();
columnSet.Attributes =
new string[] {"new_davetestid", "new_name", "createdby"};
new_davetest daveTest = (new_davetest) service.Retrieve("new_davetest", new Guid("81a570dd-f262-db11-9e36-0016353c78ef"), columnSet);

Execute Sample

whoAmIResponse whoAmIResponse = (WhoAmIResponse) service.Execute(new WhoAmIRequest());


RetrieveMultiple Sample

QueryExpression queryExpression = new QueryExpression();
queryExpression.EntityName =

// Only requested fields are returned (rest will be null)
ColumnSet columnSet =
new ColumnSet();
columnSet.Attributes =
new string[] { "new_davetestid", "new_name", "createdby", "modifiedon" };
queryExpression.ColumnSet = columnSet;

// Create a filter condition
filterExpression = new FilterExpression();
filterExpression.FilterOperator =
ConditionExpression conditionExpression = new ConditionExpression();
conditionExpression.AttributeName =
conditionExpression.Operator =
filterExpression.Conditions =
new ConditionExpression[] { conditionExpression };
queryExpression.Criteria = filterExpression;

// Set the order
OrderExpression orderExpression = new OrderExpression();
orderExpression.AttributeName =
orderExpression.OrderType =
queryExpression.Orders =
new OrderExpression[] { orderExpression };

// Do it
BusinessEntityCollection bec = service.RetrieveMultiple(queryExpression);

// Show some output
foreach (new_davetest davetest in bec.BusinessEntities)
    Console.WriteLine("Name: {0}", davetest.new_name);

Metadata Web Service

Service Methods

Filtered Views

FilteredView Example

SqlConnection connection = new SqlConnection("Data Source=mbssbpcorpsql;Initial Catalog=Payroll_Live_MSCRM;Integrated Security=SSPI");

SqlDataAdapter adapter = new SqlDataAdapter();

adapter.SelectCommand = new SqlCommand("SELECT * FROM FilteredPayroll_Employee", connection);

DataTable table = new DataTable();


Console.WriteLine("{0} rows returned", table.Rows.Count);


(table.Rows.Count > 0)

// Write out first record
foreach (DataColumn dc in table.Columns)
Console.WriteLine("{0} = {1}", dc.ColumnName, table.Rows[0][dc.ColumnName]);






Event Notes
pre/postCreate Record creation
pre/postUpdate Record update
pre/postDelete Record delete
pre/postAssign Record ownership changed
pre/postSetState ?? TODO
pre/PostMerge ?? TODO
preSend Prior to sending an e-mail
postDeliver After sending an e-mail

Sample hook (XML side)

<?xml version="1.0" encoding="utf-8" ?>

<callout.config version="2.0" xmlns="">

  <callout entity="new_myEntity" event="PostUpdate">

      <subscription assembly="MyAssembly.dll" class="MyCallouts.MyPostUpdateAccountClass">


  <callout entity="payroll_davetest" event="PreUpdate">

      <subscription assembly="DaveTest.dll" class="DaveTest.DaveCallout" />



Sample hook (.NET side)

using System;

using System.IO;

using System.Xml;

using System.Xml.Serialization;

using Microsoft.Crm.Callout;

using DaveTest.CrmSdk;

namespace DaveTest


public class DaveCallout : CrmCalloutBase


public DaveCallout()




public override PreCalloutReturnValue PreUpdate(CalloutUserContext userContext, CalloutEntityContext entityContext, ref string entityXml, ref string errorMessage)


XmlRootAttribute root = new XmlRootAttribute("BusinessEntity");

root.Namespace = "";

XmlSerializer xmlSerializer = new XmlSerializer(typeof(BusinessEntity), root);

StringReader sr = new StringReader(entityXml);

BusinessEntity entity = (BusinessEntity)xmlSerializer.Deserialize(sr);

DynamicEntity dynamicEntity = entity as DynamicEntity;

if (dynamicEntity != null)


errorMessage = String.Format("Entity fields were: \n\tpayroll_name={0}\n\tpayroll_myfield1={1}\n\tpayroll_myfield2={2}",

GetStringPropertyValue(dynamicEntity.Properties, "payroll_name"),

GetStringPropertyValue(dynamicEntity.Properties, "payroll_myfield1"),

GetStringPropertyValue(dynamicEntity.Properties, "payroll_myfield2"));




errorMessage = "Failed to de-serialize";


return PreCalloutReturnValue.Abort;



private static string GetStringPropertyValue(Property[] properties, string propertyName)


Property property = FindProperty(properties, propertyName);

if (property == null) return null;

StringProperty stringProperty = property as StringProperty;

if (stringProperty == null) return null;

return stringProperty.Value;



private static Property FindProperty(Property[] properties, string propertyName)


foreach (Property property in properties)


if (property.Name == propertyName) return property;


return null;




Note: The entityXml parameter is a a diffgram. If the attribute hasn't changed in the form, its value isn't included in the diffgram and therefore ends up as null in the above example.

Client Side Customizations


Web-site Defaults

Setting Value
Enabled HTTP Keep-Alives On
Application name Microsoft CRM
Execute permissions Scripts only
Application Pool CRMAppPool
Content expiration 3 days
Custom HTTP header [p3p privacy policy]
Authentication Method Integrated Windows authentication
(The Outlook client requires automatic login (e.g. server to be in a trusted zone). While the CRM client doesn't require this, adding it to the trusted zone is the way average user avoid manual sign ins and popup permissions)
SSL cert Not required



Edition Notes
Core Accounts, Contacts, Security, Calendar, Reporting, Extensibility
Standard Leads, Outlook Client, Correspondence, Mail Merge
Professional Quotes, Product Catalog, Workflow, GP integration

Tricks & Tips

Key Type Value
TraceEnabled DWORD 1
TraceDirectory string [Full path to an existing directory]
TraceCallStack DWORD 0
TraceRefresh DWORD 1
TraceSchedule string Hourly