我有以下代码可以使用,但我相信它只是为了获得与我的自定义“项目”实体相关联的“帐户”实体而执行多次查找。
是否可以指定要填充哪些关联实体而无需循环初始结果集?
public IList<new_project> GetAssociatedProjectsByPostcode(string postcode)
{
FilterExpression filter = new FilterExpression();
filter.FilterOperator = LogicalOperator.And;
filter.AddCondition(new ConditionExpression("new_project_zippostalcode", ConditionOperator.Equal, postcode));
QueryExpression query = new QueryExpression();
query.EntityName = new_project.EntityLogicalName;
query.ColumnSet = new ColumnSet(true);
query.Criteria = filter;
OrganizationServiceCache serviceCache = new OrganizationServiceCache(MemoryCache.Default, base.CrmConnection);
using (CachedOrganizationService service = new CachedOrganizationService(CrmConnection, serviceCache))
using (XrmServiceContext xrmServiceContext = new XrmServiceContext(service))
{
//Run the query to return the project entities in a list
IList<new_project> projects = service.RetrieveMultiple(query)
.Entities
.Select(item => item.ToEntity<new_project>())
.ToList<new_project>();
//Define the relationships we want populated
Relationship accountRel = new Relationship("new_account_new_project");
//We cannot call load property with tracking turned on
xrmServiceContext.MergeOption = MergeOption.NoTracking;
//Loop through the original list and get our associations
foreach (new_project np in projects)
xrmServiceContext.LoadProperty(np, accountRel);
return projects;
}
}
是否可以使用ServiceContext生成等效文件?
Select Project.Name, Account.Name
From Project
Join Account ON Account.Id = Project.AccountId
编辑:
在使用James下面描述的链接实体后,我现在有以下内容生成一个实体集合,其中包含在一个查询中填充的项目和帐户,但我无法将此平面数据集传输到分层对象结构中。
public IList<new_project> GetAssociatedProjectsByPostcode(string postcode)
{
FilterExpression filter = new FilterExpression();
filter.FilterOperator = LogicalOperator.And;
filter.AddCondition(new ConditionExpression("new_project_zippostalcode", ConditionOperator.BeginsWith, PostcodeUtility.RegionFromPostcode(postcode)));
QueryExpression query = new QueryExpression();
query.EntityName = new_project.EntityLogicalName;
query.ColumnSet = new ColumnSet(true);
query.Criteria = filter;
query.LinkEntities.Add(new LinkEntity(new_project.EntityLogicalName, Account.EntityLogicalName, "new_associatedaccountid", "accountid", JoinOperator.Inner));
query.LinkEntities[0].Columns = new ColumnSet(true);
query.LinkEntities[0].EntityAlias = Account.EntityLogicalName;
OrganizationServiceCache serviceCache = new OrganizationServiceCache(MemoryCache.Default, base.CrmConnection);
using (CachedOrganizationService service = new CachedOrganizationService(CrmConnection, serviceCache))
using (XrmServiceContext xrmServiceContext = new XrmServiceContext(service))
{
EntityCollection ec = service.RetrieveMultiple(query);
//*****************************************************************
//The entity collection is now populated with the accounts but they
//are just additional key value pairs
//e.g. (String)((AliasedValue)ec[0]["account.name"]).Value;
//*****************************************************************
//Turn the entity collection into our class structure
IList<new_project> projects = ec.Entities
.Select(item => item.ToEntity<new_project>())
.ToList<new_project>();
return projects;
}
}
答案 0 :(得分:2)
是的,这应该是非常直接的,你有几个选择。
FetchXml,这是一个xml语法,类似于tsql的方法,它没有所有功能,但你可以与link-entity
进行连接,如MSDN所示。
QueryExpression
,具有LinkEntities
属性,可以像MSDN一样使用。
您可以发出RetrieveRequest
,填充RelatedEntitiesQuery
,如图所示here。
修改强>
所以实体集合看起来像它返回我期望的结果(例如那些帐户的帐户和值) - 我认为typeof(ec [0])是实体?
所以它只是转换到出错的早期界限。
我非常习惯使用linq,所以我开始猜测,但是从这个example开始。
您可能只需要:
例如(来自样本):
_serviceProxy.EnableProxyTypes();
Account retrievedAccount = (Account)_serviceProxy.Retrieve("account", _accountId, cols);
retrievedAccount.Address1_PostalCode = "98052";
检索只返回一个实体(EntityCollection.Entities的类型),并且转换似乎在这里工作。
答案 1 :(得分:1)
詹姆斯伍德的评论编辑... 您可以使用Retrieve Request检索实体的子实体子实体。您必须拥有父实体的GUID才能使其工作...另一方面,您可以通过向链接实体的列集合添加列来包含子实体的值。您可以指定别名或只接受默认名称。
我已广泛使用这些扩展方法从父实体访问别名值:
// This needs to be placed in a public static class and it's namespace added as a using to whatever class you'd like to use it in
/// <summary>
/// Returns the Aliased Value for a column specified in a Linked entity
/// </summary>
/// <typeparam name="T">The type of the aliased attribute form the linked entity</typeparam>
/// <param name="entity"></param>
/// <param name="attributeName">The aliased attribute from the linked entity. Can be preappeneded with the
/// linked entities logical name and a period. ie "Contact.LastName"</param>
/// <returns></returns>
public static T GetAliasedValue<T>(this Entity entity, string attributeName)
{
string aliasedEntityName = SplitAliasedAttributeEntityName(ref attributeName);
AliasedValue aliased;
foreach (var attribute in entity.Attributes.Values)
{
aliased = attribute as AliasedValue;
if(entity.IsAttributeAliasedValue(attributeName, aliasedEntityName, aliased))
{
try
{
return (T)aliased.Value;
}
catch (InvalidCastException)
{
throw new InvalidCastException(
String.Format("Unable to cast attribute {0}.{1} from type {2} to type {3}",
aliased.EntityLogicalName, aliased.AttributeLogicalName,
typeof(T).Name, aliased.Value.GetType().Name));
}
}
}
throw new Exception("Aliased value with attribute " + attributeName +
" was not found! Only these attributes were found: " + String.Join(", ", entity.Attributes.Keys));
}
/// <summary>
/// Returns the Aliased Value for a column specified in a Linked entity, returning the default value for
/// the type if it wasn't found
/// </summary>
/// <typeparam name="T">The type of the aliased attribute form the linked entity</typeparam>
/// <param name="entity"></param>
/// <param name="attributeName">The aliased attribute from the linked entity. Can be preappeneded with the
/// linked entities logical name and a period. ie "Contact.LastName"</param>
/// <returns></returns>
public static T GetAliasedValueOrDefault<T>(this Entity entity, string attributeName)
{
T value;
if (entity.HasAliasedAttribute(attributeName))
{
value = entity.GetAliasedValue<T>(attributeName);
}
else
{
value = default(T);
}
return value;
}
private static bool IsAttributeAliasedValue(this Entity entity, string attributeName, string aliasedEntityName, AliasedValue aliased)
{
bool value =
(aliased != null &&
(aliasedEntityName == null || aliasedEntityName == aliased.EntityLogicalName) &&
aliased.AttributeLogicalName == attributeName);
/// I believe there is a bug in CRM 2011 when dealing with aggregate values of a linked entity in FetchXML.
/// Even though it is marked with an alias, the AliasedValue in the Attribute collection will use the
/// actual CRM name, rather than the aliased one, even though the AttributeCollection's key will correctly
/// use the aliased name. So if the aliased Attribute Logical Name doesn't match the assumed attribute name
/// value, check to see if the entity contains an AliasedValue with that key whose attribute logical name
/// doesn't match the key (the assumed bug), and mark it as being the aliased attribute
if (!value && aliased != null && entity.Contains(attributeName))
{
var aliasedByKey = entity[attributeName] as AliasedValue;
if (aliasedByKey != null && aliasedByKey.AttributeLogicalName != attributeName &&
Object.ReferenceEquals(aliased, aliasedByKey))
{
value = true;
}
}
return value;
}
/// <summary>
/// Returns the Aliased Value for a column specified in a Linked entity
/// </summary>
/// <typeparam name="T">The type of the aliased attribute form the linked entity</typeparam>
/// <param name="entity"></param>
/// <param name="attributeName">The aliased attribute from the linked entity. Can be preappeneded with the
/// linked entities logical name and a period. ie "Contact.LastName"</param>
/// <returns></returns>
public static bool HasAliasedAttribute(this Entity entity, string attributeName)
{
string aliasedEntityName = SplitAliasedAttributeEntityName(ref attributeName);
return entity.Attributes.Values.Any(a =>
entity.IsAttributeAliasedValue(attributeName, aliasedEntityName, a as AliasedValue));
}
/// <summary>
/// Handles spliting the attributeName if it is formated as "EntityAliasedName.AttributeName",
/// updating the attribute name and returning the aliased EntityName
/// </summary>
/// <param name="attributeName"></param>
/// <param name="aliasedEntityName"></param>
private static string SplitAliasedAttributeEntityName(ref string attributeName)
{
string aliasedEntityName = null;
if (attributeName.Contains('.'))
{
var split = attributeName.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
if (split.Length != 2)
{
throw new Exception("Attribute Name was specified for an Alaised Value with " + split.Length +
" split parts, and two were expected. Attribute Name = " + attributeName);
}
aliasedEntityName = split[0];
attributeName = split[1];
}
return aliasedEntityName;
}