Code Copied

Dynamics CRM编程系列(2)Entity创建

1. 创建Entity的必要条件

在CRM中按照路径Setting → Customizations → Customize the System → Components → New → Entity可以创建Entity

SNAGHTML17dc153

SNAGHTML17efc40

在创建Entity时,有些信息是必须输入的,在Entity创建画面的【General】和【Primary Fileld】Tab可以获取到必要信息。

Tab 必要字段 备注
General Display Name Entity的显示名称
Plural Name Entity的复数显示名称
Name Entity的逻辑名称,以new_开头
Ownership Entity的拥有者,有2个选项:User or Team, Organization
Primary Field Display Name Primary Field的显示名称
Name Primary Field的逻辑名称,以new_开头
Requirement Level Primary Field的需求级别,有3个选项:No Constraint, Business Recommended, Business Required
Type Primary Field的数据类型,固定为Single Line of Text
Format Primary Field的数据格式化方式,固定为Text
Maximum Length Primary Field的最大长度,默认为100


SNAGHTML1801710

SNAGHTML1804ec3

2. C#代码创建Entity

在使用C#代码创建Entity时,需要和画面一样,提供这些必要的字段信息。

CreateEntity方法

/// <summary>
/// 创建Entity
/// </summary>
/// <param name="entityName">Entity的逻辑名称</param>
/// <param name="entityDisplayName">Entity的显示名称</param>
/// <param name="entityPluralName">Entity的复数显示名称</param>
/// <param name="primaryAttrName">Primary Field的逻辑名称</param>
/// <param name="primaryAttrDisplayName">Primary Field的显示名称</param>
public OrganizationResponse CreateEntity(
    string entityName,
    string entityDisplayName,
    string entityPluralName,
    string primaryAttrName,
    string primaryAttrDisplayName)
{
    var request = new CreateEntityRequest()
    {
        Entity = new EntityMetadata()
        {
            SchemaName = entityName,
            DisplayName = new Label(entityDisplayName, 1033),
            DisplayCollectionName = new Label(entityPluralName, 1033),
            OwnershipType = OwnershipTypes.OrganizationOwned,
        },

        PrimaryAttribute = new StringAttributeMetadata
        {
            SchemaName = primaryAttrName,
            RequiredLevel = new AttributeRequiredLevelManagedProperty(AttributeRequiredLevel.None),
            MaxLength = 100,
            Format = StringFormat.Text,
            DisplayName = new Label(primaryAttrDisplayName, 1033)
        },
    };
    return Service.Execute(request);
}

测试一:Entity创建执行成功

[TestMethod]
public void TestCreateEntity()
{
    var metadataHelper = new MetadataHelper();
    var response = metadataHelper.CreateEntity(
            "new_bankaccount",      // Entity名称
            "Bank Account",         // Entity显示名称
            "Bank Accounts",        // Entity复数显示名称
            "new_accountname",      // PrimaryAttribute名称
            "Account Name"          // PrimaryAttribute显示名称
        );

    Assert.IsNotNull(response);
    Debug.WriteLine("EntityId:"     + response.Results["EntityId"]);
    Debug.WriteLine("AttributeId:"  + response.Results["AttributeId"]);
}

运行测试:

image

在CRM系统的Default Solution中查看新建的Entity。

SNAGHTML3206316

请注意CRM系统给Bank Account生成的字段,new_bankaccountid是Primary Key,new_accountname是我们在创建时提供的Primary Field。

通过OrganizationService创建自定义的CRM Entity时,需要提供Primary Field字段,CRM会自动根据Entity的LogicaName+”id”的格式(例如:new_bankaccountid)生成Primary Key字段。
所以不要将Primary Field和Primary Key的概念混淆。在CRM中Primary Key相当于每个人的身份证号,Primary Field则好比每个人的姓名,身份证号是由系统决定的,姓名则可以指定,我们和人交往都是通过人名认识,但系统通过身份证号识别人。

SNAGHTML335fc68

测试二:Entity创建执行失败

当将Primary Key字段的名称作为Primary Field的名称时,创建Entity会执行失败。

[TestMethod]
public void TestCreateEntity()
{
    var metadataHelper = new MetadataHelper();
    var response = metadataHelper.CreateEntity(
            "new_product",      // Entity名称
            "Product",          // Entity显示名称
            "Products",         // Entity复数显示名称
            "new_productid",    // PrimaryAttribute名称
            "Product Id"        // PrimaryAttribute显示名称
        );

    Assert.IsNotNull(response);
    Debug.WriteLine("EntityId:"     + response.Results["EntityId"]);
    Debug.WriteLine("AttributeId:"  + response.Results["AttributeId"]);
}

在运行时出现了Generic SQL error。

SNAGHTML32c1506

在CRM中按照测试代码提供的信息手动创建Entity,也会出现SQL Server错误。

image

Primary Field和Primary Key的概念我前面已经描述过了,将测试失败的代码进行简单修正。

[TestMethod]
public void TestCreateEntity()
{
    var metadataHelper = new MetadataHelper();
    var response = metadataHelper.CreateEntity(
            "new_product",      // Entity名称
            "Product",          // Entity显示名称
            "Products",         // Entity复数显示名称
            "new_productname",  // PrimaryAttribute名称
            "Product Name"      // PrimaryAttribute显示名称
        );

    Assert.IsNotNull(response);
    Debug.WriteLine("EntityId:"     + response.Results["EntityId"]);
    Debug.WriteLine("AttributeId:"  + response.Results["AttributeId"]);
}

运行测试:

image

CheckCreateEntity方法

在测试失败的例子中,CreateEntity方法没有限制调用者传入的参数,而且引发了SQL error异常,仅根据这样的异常信息我们是很难调查原因的。
与其在调用者测试的时候触发异常,还不如在CreateEntity方法中做一些验证,避免调用者传入不合适的参数,我能考虑到的验证包含6点:
    1. 验证Entity的LogicalName前缀
    2. 验证Entity的LogicalName是否都是小写字符
    3. 验证Entity的LogicalName是否已存在
    4. 验证Primary Field的LogicalName前缀
    5. 验证Primary Field的LogicalName是否和Primary Key的LogicalName重复
    6. 验证Primary Field的LogicalName是否都是小写字符

添加一个验证方法:CheckCreateEntity

/// <summary>
/// 验证创建Entity的LogicalName和Primary Field的LogicalName
/// </summary>
/// <param name="entityName">Entity的LogicalName</param>
/// <param name="primaryAttrName">Primary Field的LogicalName</param>
private bool CheckCreateEntity(string entityName, string primaryAttrName)
{
    // 验证Entity的LogicalName是否以new_开头
    if (!entityName.StartsWith("new_"))
    {
        throw new ArgumentException("The customize entity name should start with \"new_\".");
    }
    // 验证Entity的LogicalName是否都是小写字符
    if (entityName.ToLower() != entityName)
    {
        throw new ArgumentException("The customize entity name should be lower characters.");
    }
    // 验证Entity的LogicalName是否已存在
    try
    {
        var request = new RetrieveEntityRequest
        {
            EntityFilters = EntityFilters.Entity,
            LogicalName = entityName
        };
        Service.Execute(request);
        throw new ArgumentException(string.Format("The entity {0} is already exists.", entityName));
    }
    catch // 出现异常时表示该Entity的LogicaName是不存在的,可以创建
    {
    }

    // 验证Primary Field的LogicalName是否以new_开头
    if (!primaryAttrName.StartsWith("new_"))
    {
        throw new ArgumentException("The primary field name should start with \"new_\".");
    }
    // 验证Primary Field的LogicalName是否和Primary Key的LogicalName重复
    if (string.Concat(entityName, "id") == primaryAttrName)
    {
        throw new ArgumentException("The entity primary field name should not equal with primary key name.");
    }
    // 验证Primary Field的LogicalName是否都是小写字符
    if (primaryAttrName.ToLower() != primaryAttrName)
    {
        throw new ArgumentException("The primary field name should be lower characters.");
    }
    return true;
}

CreateEntity方法稍作改动:

/// <summary>
/// 创建Entity
/// </summary>
/// <param name="entityName">Entity的逻辑名称</param>
/// <param name="entityDisplayName">Entity的显示名称</param>
/// <param name="entityPluralName">Entity的复数显示名称</param>
/// <param name="primaryAttrName">Primary Field的逻辑名称</param>
/// <param name="primaryAttrDisplayName">Primary Field的显示名称</param>
public OrganizationResponse CreateEntity(
    string entityName,
    string entityDisplayName,
    string entityPluralName,
    string primaryAttrName,
    string primaryAttrDisplayName)
{
    if (CheckCreateEntity(entityName, primaryAttrName))
    {
        var request = new CreateEntityRequest()
        {
            Entity = new EntityMetadata()
            {
                SchemaName = entityName,
                DisplayName = new Label(entityDisplayName, 1033),
                DisplayCollectionName = new Label(entityPluralName, 1033),
                OwnershipType = OwnershipTypes.OrganizationOwned,
            },

            PrimaryAttribute = new StringAttributeMetadata
            {
                SchemaName = primaryAttrName,
                RequiredLevel = new AttributeRequiredLevelManagedProperty(AttributeRequiredLevel.None),
                MaxLength = 100,
                Format = StringFormat.Text,
                DisplayName = new Label(primaryAttrDisplayName, 1033)
            },
        };
        return Service.Execute(request);
    }
    return null;
}

这样我们的程序就看起来更加健壮了,当调用者因不熟悉一些规则而传入不合法的参数时,CheckCreateEntity方法能够直观地将异常信息反映该调用者。

3. 参考和源码

参考链接:http://msdn.microsoft.com/en-us/library/microsoft.xrm.sdk.messages.createentityrequest(v=crm.6).aspx
下载链接:http://blog.64cm.com/source/CRM/Dynamics CRM Programming(2).zip