How to query multiple entities with a single QueryExpression - c#

I am trying to run a microsoft dynamics crm 4.0 query. This works as expected when I generate a QueryExpression for "ONE" specific entity as I had done before. The issue however is how do i define more than one entity so i can apply logic that i have in another method? An example or illistration would be helpful.
so what i have is in this format:
static BusinessEntityCollection GetData(CrmService service)
{
cols = new ColumnSet();
cols.Attributes = new string[] { "x", "y", "z"};
FilterExpression filter = new FilterExpression();
filter.FilterOperator = LogicalOperator.And;
QueryExpression query = new QueryExpression();
query.EntityName = EntityName.incident.ToString();
// i am trying to add something like the below
query.EntityName = EntityName.account.toString();
query.ColumnSet = cols;
query.Criteria = filter;
return service.RetrieveMultiple(query);
}
The restriction I am facing is I can only query one entity and I need a solution or workaround to access and query multiple entities. Your help is much appreciated.

Simple answer: you can't. You could only query one entity at one time.
A query expression is used for single-object searches, for example, to search for all accounts that match certain search criteria.
See how to build queries.
You have to combine multiple requests for the entities you would like to get.
The same restriction exists for FetchXML. It is basically the serialized form of a QueryExpression. See how to use FetchXML

Related

C# crm linQ LinkEntities

I need to query from a CRM entity some values where a linked entity contains a string inside its name.
I try to explain:
I have new_supplycontract table.
This table, has an EntityReference named new_pod
The new_pod entity, has two fields: new_citypod and new_street
new_citypod points to another entity named new_city
new_street points to another entity named new_street
I need to query the new_supplycontract table to retrieve only the records whose new_pod contains a street which name contains a string I pass and a city which name contains another string i pass.
I know this code works for retrieving all new_supplycontract entities whose two text fields "new_city" and "new_address" are like to two strings passed.
QueryExpression query = new QueryExpression(new_supplycontract.EntityLogicalName);
query.ColumnSet = new ColumnSet(true);
query.Criteria.AddCondition("new_city", ConditionOperator.NotNull);
query.Criteria.AddCondition("new_address", ConditionOperator.NotNull);
query.LinkEntities.Add(new LinkEntity(new_supplycontract.EntityLogicalName, "new_comune", "new_city", "new_comuneid", JoinOperator.Inner));
query.LinkEntities[0].Columns.AddColumns("new_name");
query.LinkEntities[0].EntityAlias = "comuneTemp";
query.LinkEntities[0].LinkCriteria.AddCondition("new_name", ConditionOperator.Like, "%" + comune + "%");
query.LinkEntities.Add(new LinkEntity(new_supplycontract.EntityLogicalName, new_via.EntityLogicalName, "new_address", "new_viaid", JoinOperator.Inner));
query.LinkEntities[1].Columns.AddColumns("new_name");
query.LinkEntities[1].EntityAlias = "viaTemp";
query.LinkEntities[1].LinkCriteria.AddCondition("new_name", ConditionOperator.Like, "%" + via + "%");
DataCollection<Entity> entities = service.RetrieveMultiple(query).Entities;
But I don't really know how to use this code for my goal. I don't know how to filter an entityreference's entityreference.
Any help will be appreciated
LinkedEntities can have nested LinkedEntities in CRM 2011. This was changed since 2013 where an EntityName attribute was introduced and there wasn't a need for having nested entities.
But basically, you could start from the supplycontract entity, then add a linked entity against your new_pod entity. From that Linked Entity, you'll need to add 2 linked entities, one to the new_city entity and another one to the new_street entity. Each of these 2 linked entities will need a contains condition expression where you pass the 2 strings you want to use for filtering.
Ex:
QueryExpression query = new QueryExpression(new_supplycontract.EntityLogicalName);
query.ColumnSet = new ColumnSet(true);
var le = query.LinkEntities.Add(new LinkEntity(new_pod.EntityLogicalName, "new_pod", "new_pod", "new_podid", JoinOperator.Inner));
var lePod = le.LinkEntities.Add(new LinkEntity(new_pod.EntityLogicalName, "new_city", "new_citypod", "new_cityid", JoinOperator.Inner));
var leCity = le.LinkEntities.Add(new LinkEntity(new_pod.EntityLogicalName, "new_street", "new_street", "new_streetid", JoinOperator.Inner));
//Add conditions to each nested linked entity now as above.
Now, I personally prefer LINQ cause the query is much more easier to read than using LinkedEntities.
Hope this helps.
I've ran into countless issues with LINQ to CRM, mostly due to my misunderstanding of how it works. Unfortunately, Query Expressions are extremely verbose and hard to read. I created and use the DLaB.Xrm library to make my life simpler (and more typed, I love me some EarlyBound Dev)
Here is your query using DLaB.Xrm:
var qe = QueryExpressionFactory.Create<new_supplycontract>();
var podLink = qe.AddLink<new_pod>(new_supplycontract.Fields.new_Pod, new_pod.Fields.Id);
podLink.AddLink<new_city>(new_pod.Fields.new_citypod, // This is the attribute of the "from" entity to join on
new_city.Fields.Id, // This is the attribute of the "to" entity to join on. If name is identical, this parameter can be removed
new ColumnSet(new_city.Fields.new_name)) // AliasedValue to add to the result
.LinkCriteria.AddCondition("new_name", ConditionOperator.Like, "%" + comune + "%");
podLink.AddLink<new_street>(new_pod.Fields.new_street,
new_street.Fields.Id,
ColumnSet(new_street.Fields.new_name))
.LinkCriteria.AddCondition("new_name", ConditionOperator.Like, "%" + via + "%");
var leads = service.GetEntities(qe);
You need to chain the link entities. Here's an example:
ConditionExpression condition3 = new ConditionExpression();
...
LinkEntity link3 = new LinkEntity();
...
link3.LinkCriteria.Conditions.Add(condition3);
ConditionExpression condition2 = new ConditionExpression();
...
LinkEntity link2 = new LinkEntity();
...
link2.LinkCriteria.Conditions.Add(condition2);
LinkEntity link1 = new LinkEntity();
...
link1.LinkEntities.Add(link2);
link1.LinkEntities.Add(link3);
QueryExpression query = new QueryExpression("");
...
query.LinkEntities.Add(link1);
DataCollection<Entity> entities = service.RetrieveMultiple(query).Entities;
It helps to write them separately and use .Criteria.Conditions.Add() instead of .Criteria.AddCondition()

Retrieve all child/descendant/related entities programmatically

How do I retrieve all of the related entities of a source entity in CRM Dynamics 2011, in C#?
thanks
FetchXml is overkill for this requirement, but obviously if you want to construct the FetchXml and use it for your QueryBase in place of the QueryExpression I'm showing you are free to do that. The logic remains the same.
//Assumes you have a Entity() object of the parent entity
//somehow you have to know the parent entity record's Id
Guid parentId = parentEntity.Id;
var query = new QueryExpression("new_childentity");
query.Criteria.AddCondition(new ConditionExpression("new_lookupfield", ConditionOperator.Equal, parentId));
query.ColumnSet = new ColumnSet(true);
var results = service.RetrieveMultiple(query);
if (results.Entities.Any())
{
//Do your processing here
}
else
{
//Do whatever when there are no child entities
}

How can I get all the activities for an account?

I have a program that I have written that gets the data from Dynamics CRM 2013 online. But I am running into one issue where a query to get the activities for an account only returns a subset instead of all the activities. The query expression is this
private QueryExpression CreateActivityQuery(Guid id)
{
QueryExpression query = new QueryExpression()
{
Distinct = true,
EntityName = Cd2Sf.ActivityPointer.EntityLogicalName,
ColumnSet = new ColumnSet(true)
};
query.Criteria = new FilterExpression();
query.Criteria.AddCondition("regardingobjectid", ConditionOperator.Equal, id);
return query;
}
Where the id is the account id. I had first tried using the activityparty where the party id was equal to the account id and then I tried using the regardingobjectid and lastly then tried the Rollup method with the extended related entities but all produce the same result.
#region Create RollupRequest
// Create RollupRequest
RollupRequest rollupRequest = new RollupRequest();
rollupRequest.Query = qexp;
rollupRequest.Target = new EntityReference("account", acct.Id);
rollupRequest.RollupType = RollupType.Extended;
#endregion Create RollupRequest
#region Execute RollupRequest
// Execute RollupRequest
RollupResponse rollupResponse = (RollupResponse)service.Execute(rollupRequest);
#endregion Execute RollupRequest
#region Show RollupResponse results
ShowActivities(rollupResponse.EntityCollection, percent);
#endregion Show RollupResponse results
Do I need to use contacts as well to get all the activities that are associated to the account, i.e. use the activityparty and match the to/from/sender/cc/bcc/etc with contacts for an account? I had tried to use the contact id as the regardingobjectid but that still does not account for all of the activities.
It appears that the web page for dynamics crm online when viewing the account and the activities that are on that page are more than what the above query gets alone.
How do I get the other activities, the ones that seem to be not directly related to the account?
My original answer doesn't answer the question - I've abbreviated it here.
Sarah Champ's post on using outer joins in your fetchxml is really good:
http://blogs.msdn.com/b/crminthefield/archive/2013/07/01/dynamic-activity-reporting-using-fetchxml.aspx
You have to use a FetchExpression rather than a QueryExpression, but you can use it the same way with a RetrieveMultipleRequest now.
FetchExpression example:
string fetch = "<fetch xml string>"
var query = new FetchExpression(fetch);
var request = new RetrieveMultipleRequest();
request.Query = query;
var entities = ((RetrieveMultipleResponse)service.Execute(request)).EntityCollection.Entities;
foreach (var entity in entities)
{
Console.WriteLine(entity.GetAttributeValue<string>("subject"));
}

Retrieve Filter Conditions in Views in CRM 2011 using query expression (C#)

Am developing a windows form which resembles the look up view on CRM 2011.
For this I do the following
1) Show the relevant records (of opportunity entity) in a datagirdview
2) Also have a dropdownlist that binds to the views (View.Name) to allow the user to choose the view according to which i intend to change the filter on my datagrid view.
Am stuck with the following.Am able to retrieve the views on the "Opporutunity" entity using the following code
ConditionExpression condition1 = new ConditionExpression()
{
AttributeName = "querytype",
Operator = ConditionOperator.Equal,
Values = { 0 }
};
ConditionExpression condition2 = new ConditionExpression()
{
AttributeName = "returnedtypecode",
Operator = ConditionOperator.Equal,
Values = { Opportunity.EntityTypeCode }
};
FilterExpression filter = new FilterExpression();
filter.Conditions.Add(condition1);
filter.Conditions.Add(condition2);
QueryExpression queryToRetrieveViews = new QueryExpression
{
ColumnSet = new ColumnSet("savedqueryid", "name", "querytype", "isdefault", "returnedtypecode", "isquickfindquery"),
EntityName = SavedQuery.EntityLogicalName,
Criteria = filter
};
RetrieveMultipleRequest retrieveSavedViewsRequest = new RetrieveMultipleRequest { Query = queryToRetrieveViews };
RetrieveMultipleResponse retrieveSavedViewsResponse = (RetrieveMultipleResponse)crm.Execute(retrieveSavedViewsRequest);
DataCollection<Entity> savedViews = retrieveSavedViewsResponse.EntityCollection.Entities;
foreach (Entity ent in savedViews){...}
I did a quick watch but am unable to find the attribute that has the filter conditions as present in CRM. I mean what i intend to look at is something like this sayfor e.g. open opportunities the filter would be "statecode=0".
Is it possible to fetch the associated filters? -sorry by Associated filters i mean filters of the view
You can filter by whatever attribute you'd like so I'm not sure what you mean by the associated filters. If you mean the filter of the actual view you won't find it. Views are stored in XML, so you'll have to retrieve the the FetchXML for the saved view and parse the XML to see it's filter.

dynamic query to azure tables

I'm using azure table storage to store blog posts. Each blog post can have different tags.
So I'm going to have three different tables.
One which will store the blog posts.
One to store the tags
One that will store the relation between the tags and posts
So my question is as following, is it possible to create dynamic search queuries? Because I do not know until at run time how many tags I want to search.
As I understand it you can only query azure table using LINQ. Or can I input a string query that I can change dynamically?
UPDATE
Here's some example data that's in the blog table
PartitionKey,RowKey,Timestamp,Content,FromUser,Tags
user1, 1, 2012-08-08 13:57:23, "Hello World", "root", "yellow,red"
blogTag table
PartitionKey,RowKey,Timestamp,TagId,TagName
"red", "red", 2012-08-08 11:40:29, 1, red
"yellow", "yellow", 2012-08-08 11:40:29, 2, yellow
relation table
PartitionKey,RowKey,Timestamp,DataId,TagId
1, 1, 2012-08-08 11:40:29, 1, 1
2, 1, 2012-08-08 13:57:23, 1, 2
One usage example of these tables is for example when I want to get all blog post with certain tag.
I have to query the tagId from the blogTag table
There after I need to search in the relation table for the dataId
Lastly I need to search blog table for blog post with that dataId
I'm using LINQ to perform the query and it looks like following
CloudTableQuery<DataTag> tagIds = (from e in ctx2.CreateQuery<DataTag>("datatags")
where e.PartitionKey == tags
select e).AsTableServiceQuery<DataTag>();
I tried Gaurav Mantri suggestion of using filter, and it works. But I'm afraid of how the effiency of that will be. And about the limitation of 15 discrete comparison that's only allowed.
You can simple build where clause and pass to where method for example:
var whereClause="(PartitionKey eq 'Key1') and (PartitionKey eq 'Key2')"
CloudStorageAccount storageAccount = CloudStorageAccount.Parse("AccountDetails");
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference(<TableName>);
table.CreateIfNotExists();
TableQuery<YourAzureTableEntity> query =
new TableQuery<YourAzureTableEntity>()
.Where(whereClause));
var list = table.ExecuteQuery(query).ToList();
I am also facing exactly same problem. I did find one solution which I am pasting below:
public static IEnumerable<T> Get(CloudStorageAccount storageAccount, string tableName, string filter)
{
string tableEndpoint = storageAccount.TableEndpoint.AbsoluteUri;
var tableServiceContext = new TableServiceContext(tableEndpoint, storageAccount.Credentials);
string query = string.Format("{0}{1}()?filter={2}", tableEndpoint, tableName, filter);
var queryResponse = tableServiceContext.Execute<T>(new Uri(query)) as QueryOperationResponse<T>;
return queryResponse.ToList();
}
Basically it utilizes DataServiceContext's Execute(Uri) method: http://msdn.microsoft.com/en-us/library/cc646700.aspx.
You would need to specify the filter condition as you would do if you're invoking the query functionality through REST API (e.g. PartitionKey eq 'mypk' and RowKey ge 'myrk').
Not sure if this is the best solution :) Looking forward to comments on this.
It is possible, but it may not be a good idea. Adding multiple query parameters like that always results in a table scan. That's probably OK in a small table, but if your tables are going to be large it will be very slow. For large tables, you're better off running a separate query for each key combination.
That said, you can build a dynamic query with some LINQ magic. Here is the helper class I've used for that:
public class LinqBuilder
{
/// <summary>
/// Build a LINQ Expression that roughly matches the SQL IN() operator
/// </summary>
/// <param name="columnValues">The values to filter for</param>
/// <returns>An expression that can be passed to the LINQ .Where() method</returns>
public static Expression<Func<RowType, bool>> BuildListFilter<RowType, ColumnType>(string filterColumnName, IEnumerable<ColumnType> columnValues)
{
ParameterExpression rowParam = Expression.Parameter(typeof(RowType), "r");
MemberExpression column = Expression.Property(rowParam, filterColumnName);
BinaryExpression filter = null;
foreach (ColumnType columnValue in columnValues)
{
BinaryExpression newFilterClause = Expression.Equal(column, Expression.Constant(columnValue));
if (filter != null)
{
filter = Expression.Or(filter, newFilterClause);
}
else
{
filter = newFilterClause;
}
}
return Expression.Lambda<Func<RowType, bool>>(filter, rowParam);
}
public static Expression<Func<RowType, bool>> BuildComparisonFilter<RowType, ColumnType>(string filterColumnName, Func<MemberExpression, BinaryExpression> buildComparison)
{
ParameterExpression rowParam = Expression.Parameter(typeof(RowType), "r");
MemberExpression column = Expression.Property(rowParam, filterColumnName);
BinaryExpression filter = buildComparison(column);
return Expression.Lambda<Func<RowType, bool>>(filter, rowParam);
}
}
You would use it something like this:
var whereClause = BuildListFilter(queryColumnName, columnValues);
CloudTableQuery<RowType> query = (from r in tableServiceContext.CreateQuery<MyRow>("MyTable")
where r.PartitionKey == partitionKey
select r)
.Where(whereClause) //Add in our multiple where clauses
.AsTableServiceQuery(); //Convert to table service query
var results = query.ToList();
Note also that the Table service enforces a maximum number of constraints per query. The documented maximum is 15 per query, but when I last tried this (which was some time ago) the actual maximum was 14.
Building something like this in table storage is quite cumbersome; akin to forcing a square peg in a round hole.
Instead you could considered using Blob storage to store your Blogs and Lucene.NET to implement your search of tags. Lucene would also allow more complex searches like (Tag = "A" and Tag = "B" and Tag != "C") and in addition would also allow searching over the blog text itself, if you so choose.
http://code.msdn.microsoft.com/windowsazure/Azure-Library-for-83562538

Categories