Dynamics CRM C# SDK - 试图获取报价行的名称,但字段不存在?
Dynamics CRM C# SDK - Trying to get name of Quote Line, but field does not exist?
我想列出报价的报价行,同时保持界面足够通用,以便能够类似地显示其他实体。
为此,我尝试使用 EntityMetadata 中的 PrimaryNameAttribute。适用于大多数实体,但对于 QuoteDetail,PrimaryNameAttribute 是 "productidname" - 作为 QuoteDetail 实体的一部分实际上并不存在的属性。
此字段 "productidname" 也是其他实体(OpportunityProduct、SalesOrderDetail)的 PrimaryNameAttribute,在这些情况下,该字段也缺失。
什么给了?我觉得我已经搜索了所有 Google 并且之前似乎没有人 运行 进入过这个问题,所以,也许我遗漏了一些简单的东西。
这是 MSDN 的 QuoteDetail 实体页面,显示了我在说什么:https://msdn.microsoft.com/en-us/library/mt607959.aspx请注意,PrimaryNameAttribute 未在页面的其他任何位置列出。
QuoteDetail(如 SalesOrderDetail 和 OpportunityProduct)的特殊之处在于它可以是目录产品项或写入项的关系实体,并从 productidname
(产品来自目录)或 productdescription
(写入产品)。您需要加入相关产品才能获得其名称。
如果您要开发一个泛型 solution/interface,这是您需要为每个 Lookup 属性解决的问题。
如果您只需要查找中连接的记录中的 PrimaryNameAttribute,您可以从 "main" 实体的 FormattedValues
集合中检索名称:
string productname = quotedetail.FormattedValues["productid"];
还 better/safer:
string productname;
quotedetail.FormattedValues.TryGetValue("productid", out productname);
任意实体的处理属性可以如下所示:
foreach (var key in record.Attributes.Keys)
{
if (record.FormattedValues.ContainsKey(key))
{
string formattedvalue;
if (record.FormattedValues.TryGetValue(key, out formattedvalue))
{
Console.WriteLine(formattedvalue); // use formattedvalue string
}
continue; // skip to next field when found in formatted values
}
object attributevalue;
record.Attributes.TryGetValue(key, out attributevalue);
object actualvalue;
string actualtext = string.Empty;
// handle AliasedValue fields from JOINed/LinkEntities
if (attributevalue.GetType().Name == "AliasedValue")
{
actualvalue = ((AliasedValue)attributevalue).Value;
}
else
{
actualvalue = attributevalue;
}
switch (actualvalue.GetType().Name)
{
case "EntityReference":
actualtext = ((EntityReference)actualvalue).Name; // this will catch Lookup values not contained in FormattedValues when you just created them
break;
case "DateTime":
actualtext = string.Format("{0:dd.MM.yyyy}", ((DateTime)actualvalue).ToLocalTime()); // ... any other dateTime format you'd like
break;
case "Guid":
actualtext = string.Format("{0:D}", actualvalue); // Entity Primary key
break;
default:
actualtext = (string)actualvalue; // anything else
break;
}
Console.WriteLine(actualtext);
}
您仍然需要处理新分配的 OptionSetValue
和 Money
属性(类似于 EntityReference
属性),因为它们通常会从 FormattedValues
中提取。
由于此示例处理的是现有的 crm 数据,因此您需要意识到您的实体可能不会包含所有属性的陷阱,因此您可能不想迭代 .Attributes.Keys
,而是希望遍历预定义的属性名称集合。
我的个人策略通常是创建轻量级 ORM 以在类型化对象和 CRM 实体之间进行映射,但这不符合您对通用接口的要求。
对于像你这样的情况,无论是这个还是那个属性,我都会使用语法糖来工作:
string pn = qd.GetAttributeValue<string>("productdesription") ?? (qd.GetAttributeValue<EntityReference>("productid") ?? new EntityReference { Name = string.Empty }).Name;
尝试获取Write-In产品名称;如果为空,则尝试获取查找名称,如果为空,则从假 EntityReference 获取空字符串。
这允许相当无摩擦的编码并很好地解决了 "either this or that attribute"。
我想列出报价的报价行,同时保持界面足够通用,以便能够类似地显示其他实体。
为此,我尝试使用 EntityMetadata 中的 PrimaryNameAttribute。适用于大多数实体,但对于 QuoteDetail,PrimaryNameAttribute 是 "productidname" - 作为 QuoteDetail 实体的一部分实际上并不存在的属性。
此字段 "productidname" 也是其他实体(OpportunityProduct、SalesOrderDetail)的 PrimaryNameAttribute,在这些情况下,该字段也缺失。
什么给了?我觉得我已经搜索了所有 Google 并且之前似乎没有人 运行 进入过这个问题,所以,也许我遗漏了一些简单的东西。
这是 MSDN 的 QuoteDetail 实体页面,显示了我在说什么:https://msdn.microsoft.com/en-us/library/mt607959.aspx请注意,PrimaryNameAttribute 未在页面的其他任何位置列出。
QuoteDetail(如 SalesOrderDetail 和 OpportunityProduct)的特殊之处在于它可以是目录产品项或写入项的关系实体,并从 productidname
(产品来自目录)或 productdescription
(写入产品)。您需要加入相关产品才能获得其名称。
如果您要开发一个泛型 solution/interface,这是您需要为每个 Lookup 属性解决的问题。
如果您只需要查找中连接的记录中的 PrimaryNameAttribute,您可以从 "main" 实体的 FormattedValues
集合中检索名称:
string productname = quotedetail.FormattedValues["productid"];
还 better/safer:
string productname;
quotedetail.FormattedValues.TryGetValue("productid", out productname);
任意实体的处理属性可以如下所示:
foreach (var key in record.Attributes.Keys)
{
if (record.FormattedValues.ContainsKey(key))
{
string formattedvalue;
if (record.FormattedValues.TryGetValue(key, out formattedvalue))
{
Console.WriteLine(formattedvalue); // use formattedvalue string
}
continue; // skip to next field when found in formatted values
}
object attributevalue;
record.Attributes.TryGetValue(key, out attributevalue);
object actualvalue;
string actualtext = string.Empty;
// handle AliasedValue fields from JOINed/LinkEntities
if (attributevalue.GetType().Name == "AliasedValue")
{
actualvalue = ((AliasedValue)attributevalue).Value;
}
else
{
actualvalue = attributevalue;
}
switch (actualvalue.GetType().Name)
{
case "EntityReference":
actualtext = ((EntityReference)actualvalue).Name; // this will catch Lookup values not contained in FormattedValues when you just created them
break;
case "DateTime":
actualtext = string.Format("{0:dd.MM.yyyy}", ((DateTime)actualvalue).ToLocalTime()); // ... any other dateTime format you'd like
break;
case "Guid":
actualtext = string.Format("{0:D}", actualvalue); // Entity Primary key
break;
default:
actualtext = (string)actualvalue; // anything else
break;
}
Console.WriteLine(actualtext);
}
您仍然需要处理新分配的 OptionSetValue
和 Money
属性(类似于 EntityReference
属性),因为它们通常会从 FormattedValues
中提取。
由于此示例处理的是现有的 crm 数据,因此您需要意识到您的实体可能不会包含所有属性的陷阱,因此您可能不想迭代 .Attributes.Keys
,而是希望遍历预定义的属性名称集合。
我的个人策略通常是创建轻量级 ORM 以在类型化对象和 CRM 实体之间进行映射,但这不符合您对通用接口的要求。
对于像你这样的情况,无论是这个还是那个属性,我都会使用语法糖来工作:
string pn = qd.GetAttributeValue<string>("productdesription") ?? (qd.GetAttributeValue<EntityReference>("productid") ?? new EntityReference { Name = string.Empty }).Name;
尝试获取Write-In产品名称;如果为空,则尝试获取查找名称,如果为空,则从假 EntityReference 获取空字符串。
这允许相当无摩擦的编码并很好地解决了 "either this or that attribute"。