get related data from ms dynamics crm using XRM SDK - c#

I'm trying to retrieve data from crm in a .net application, using the SDK.
I've managed to do simple queries to retrieve lists, but I would now like to get the related entities with items, rather than the ids.
I have tried things like
QueryExpression query = new QueryExpression
{
EntityName = "opportunity",
....
LinkEntity linkEntityAccount = new LinkEntity()
{
LinkFromEntityName = "opportunity",
LinkFromAttributeName = "opportunityid",
LinkToEntityName = "serviceappointment",
LinkToAttributeName = "regardingobjectid",
JoinOperator = JoinOperator.Inner,
Columns = new ColumnSet(new string[] { "scheduledstart", "scheduledend" }),
EntityAlias = "service"
};
query.LinkEntities.Add(linkEntityAccount);
(This will return a collection of entities from the opportunity table)
However the LinkedEntities just put the two columns in the returns entities.
What i would like is (say for this example) is a entity.serviceappointment to be the the entity containing the service appointment entity/data. Instead of in entity there being fields such as service.scheduledstart and service.scheduledend
I have looked at the Relationship and RelationshipQueryCollection things in the SDK but i have been unable to setup a query that will do the query, without first getting the opportunity entities. But it looks like that maybe what I need? I'm not sure.
Is this even possible? Or should I just continue to query entities individually?
Thanks

In the QueryExpression the LinkEntity represents a join. That's why the fields of the joined table are in the Entity row. They can be distinguished from the 'real' entity attributes by the fact that their names are prefixed (including a dot) and their values are wrapped in an AliasedValue object.
It is possible to unwrap them and create strong typed Entity objects, but you will need to write the code yourself.
Alternatively you can consider a few other options:
Query for serviceappointment records and join the opportunity records.
Retrieve opportunity records one by one using the RetrieveRequest and include the query for the related service appointments in the request. (See also this discussion on StackOverflow.)
Create an Action returning all data you need in a convenient OrganizationResponse.

There's no automatic way to get the entire linked entity data (as an Entity object) that I know of (that's not to say it's impossible, mind you).
But I think it'd be a lot easier to just query the data you need in another request.
Find the list of opportunities you need
Use the regarding object IDs as the parameter of an "IN" filter for the second query.

Related

Populating DbSet<TEntity>.Local with only specified fields

In Linq to Sql, I would download only a subset of fields for processing in order to reduce query time. Something like this...
var local_data = from row in context.MyTable
select new {
ID = row.ID,
Name = row.Name,
EMAIL = row.EMAIL
};
And then I would simply convert the projected data into a POCO collection...
foreach(var item in local_data){
collection.Add(
new MyTable(){
ID = item.ID,
NAME = item.NAME,
EMAIL = item.EMAIL
};
);
}
This is extremely useful when dealing with massive, unwieldly table records where I only want to pull a handful of columns. When I heard about DbSet<TEntity>.Local, I was eager to switch over from Linq2SQL, but I can't seem to find the version of this new streamlined caching system that allows me to narrow the query scope to specific columns. How would I go about this?
caching system that allows me to narrow the query scope to specific columns
Sorry, the answer is: not possible.
The reason is that EF's internal cache is used for tracking entities, full entities. Being able to access these cached entities through the Local collection is a mere bonus that was introduced with the DbContext API. The cache doesn't exist because of it. The cache is for change tracking.
When EF materialized an entity from the database, it stores its original values into the change tracker and also frequently stores copies of its current values. When it's time to save changes, these values are compared and SQL statements are generated accordingly to store the changes.
Now you know this, you'll understand that EF can't store party populated entities into its cache. How should EF carry out change tracking if an entity can have any random collection of original values and current values?
Also, the result of a projection -- select new -- is never tracked (cached) and, thus, not accessible through a Local collection.
So in this respect you won't gain much by moving to EF.

Filling one to many relationship using using Dapper or via Linq

Entity - AllSalesTerritory contains List<MySalesPerson> representing one to many relationship. I have Sql query to fetch the data where the two entities are mapped using a column TerritoryId. I use a following code to fill the entity using Dapper micro ORM:
List<AllSalesTerritory> allSalesTerrotories = _connection.Query<AllSalesTerritory, MySalesPerson, AllSalesTerritory>
(query, (pd, pp) =>
{
pd.SalesPersons.Add(pp);
return pd;
}, splitOn: "BusinessEntityId")
.ToList();
BusinessEntityId is the beginning column for SalesPerson entity on executing the Sql statement
Challenge that I face is, this kind of code helps in easily filling one to one relation, here I get just one value in each List<MySalesPerson>, instead of aggregating those values in the collection, essentially the same result as that of SQL join query. I can easily resolve the issue using a simple foreach loop and aggregating the values for MySalesPerson. However, I want to figure out:
Can Dapper automatically help me achieve it, tried few extensions, but they did not work as expected
Can a Linq code do it for me, since this is somewhat reverse of a SelectMany on an entity with one to many relationship
You can use a dictionary to keep track of the unique AllSalesTerritory objects. Assuming that the TerritoryId property is an int this would work.
var territories = new Dictionary<int, AllSalesTerritory>()
_connection.Query<AllSalesTerritory, MySalesPerson, AllSalesTerritory>
(query, (pd, pp) =>
{
AllSalesTerritory territory;
if(!territories.TryGetValue(pd.TerritoryId, out territory))
{
territories.Add(pd.TerritoryId, territory = pd);
}
territory.SalesPersons.Add(pp);
return territory;
}, splitOn: "BusinessEntityId");
List<AllSalesTerritory> allSalesTerrotories = territories.Values.ToList();
Basically what happens here is that Dapper will return one AllSalesTerritory and one MySalesPerson for each row in the results of your query. We then use a dictionary to see if the current AllSalesTerritory (pd) has been seen before based on the TerritoryId. If so then the local territory variable is assigned the reference to that object. If not then we assign pd to territory and then add that to the dictionary. Then we just add the current MySalesPerson (pp) to the territory.SalesPersons list.

SSIS script: Associating 2 records (one to many relationship) using CRM 4.0 (SDK) web reference

How can i link 2 records using a C sharp script in SSIS in a one to many relationship?
There is some good info out there to do this using CRM 2011 with EntityReference OR Relationship Classes. Unfortunately i cannot use these classes as i am doing this in SSIS 2008 .
Perhaps this can be done with a Lookup or Moniker but usure of detail.
Thanks
If you are able to use the Lookup class from Crm 4 its pretty easy to build a relationship for a 1 to Many, its just like setting any other attribute really.
To get the guid of the another record from Crm you will need to issue a RetrieveMultiple with a QueryExpression.
For example:
//Build the QueryExpression, the condition should give us a single record
QueryExpression query = new QueryExpression("contact");
query.Criteria = new FilterExpression();
query.Criteria.AddCondition(new ConditionExpression("someidfield", ConditionOperator.Equal, "ABC123");
BusinessEntityCollection entities = service.RetrieveMultiple(query);
//In theory we could get multiple records here, but we will assume we only get the one
DynamicEntity contact = (DynamicEntity)entities.BusinessEntities.First();
Guid existingContactId = (Guid)contact["contactid"];
Lookup lookupToExisitingContact = new Lookup();
lookupToExisitingContact.Value = existingContactId;
lookupToExisitingContact.type = "contact";
DynamicEntity newContact = new DynamicEntity("contact");
newContact["firstname"] = "James";
newContact["parentcontactid"] = lookupToExisitingContact;
service.Create(newContact);
I use both the business id and guid. See below. It still needs some work but i feel like i am going in the right direction.
First Merge Join and split checks if order already exist in CRM. In my case if it does than I don't want to take any action. The second Merge Join 1 actually does nothing but just gets the GUID of the contact using business key.

CRM 2011: Limitation of query expression?

I believe the answer to this question may be to use Linq to Sql, but wanted to see if this is something which is possible using QueryExpressions:-
I create a query expression which queries against Entity A, it also links to Entity B (via LinkEntity) and imposes additional criteria. It is possible to retrieve columns from Entity B by adding the appropriate attribute names. However, it will only retrieve the linked entity (inner join).
Is it possible using QueryExpression to retrieve all related records (and required columns) from Entity B related to Entity A (e.g. all cases associated with contact where contact passes specified criteria). Normally I would consider inverting the query and searching for Entity B relatig to Entity A with the appropriate LinkEntity Conditions, but there are a number of linked entities which I would like to retrieve for the same contact query.
So I'm left with some options:-
(1) Perform a second query (not ideal when iterating over a large number of results from the initial query),
(2) Perform a query using Linq to CRM on the filtered views,
(3) A different method entirely?
Any thoughts would be appreciated.
EDIT:
I ended up using Linq-to-Sql to complete this task and the code used is similar to that below (albeit with a few more joins for the actual query!):-
var dataCollection = (from eA in xrmServiceContext.EntityASet
join eB in xrmServiceContext.EntityBSet on new EntityReference(EntityA.EntityLogicalName, eA.Id) equals (EntityReference)eB.EntityBLookupToEntityA
select new
{
Id = eA.Id,
EntityBInterestingAttribute = eB.InterestingAttributeName
}
So this will bring back a row per Entity A, per Entity B. To make things easier I then defined a custom class "MyEntityAClass" which had properties which were Lists so I could return one object for filling of GridView etc. This is more to do with the processing of these results though so I haven't posted that code here.
I hope that makes sense. Essentially, it is getting the multiple rows per record a la SQL which makes this method work.
QueryExpression can only return fields from one type of entity, the one specified in QueryExpression.EntityName.
You can use FetchXML which allows you to also get the fields of any link entities, which would be an option 3 for you, unfortunately it returns the data as XML which you would then have to parse yourself.
It might be quicker to run the FetchXML, but it will take longet to write and test, and its not the easiest thing to maintain either.
Sample Code, this gets the first 101 of all Cases that are active for all accounts that are active
string fetch = "<fetch count='101' mapping='logical'><entity name='account'><filter type='and'><condition attribute='statecode' operator='eq' value='1'/></filter><link-entity name='incident' from='customerid' to='accountid'><all-attributes/><filter type='and'><condition attribute='statecode' operator='eq' value='1'/></filter></link-entity></entity></fetch>";
string data = yourCrmServiceObject.Fetch(fetch);

Select clause containing non-EF method calls

I'm having trouble building an Entity Framework LINQ query whose select clause contains method calls to non-EF objects.
The code below is part of an app used to transform data from one DBMS into a different schema on another DBMS. In the code below, Role is my custom class unrelated to the DBMS, and the other classes are all generated by Entity Framework from my DB schema:
// set up ObjectContext's for Old and new DB schemas
var New = new NewModel.NewEntities();
var Old = new OldModel.OldEntities();
// cache all Role names and IDs in the new-schema roles table into a dictionary
var newRoles = New.roles.ToDictionary(row => row.rolename, row => row.roleid);
// create a list or Role objects where Name is name in the old DB, while
// ID is the ID corresponding to that name in the new DB
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] };
var list = roles.ToList();
But calling ToList gives me this NotSupportedException:
LINQ to Entities does not recognize
the method 'Int32
get_Item(System.String)' method, and
this method cannot be translated into
a store expression
Sounds like LINQ-to-Entities is barfing on my call to pull the value out of the dictionary given the name as a key. I admittedly don't understand enough about EF to know why this is a problem.
I'm using devart's dotConnect for PostgreSQL entity framework provider, although I assume at this point that this is not a DBMS-specific issue.
I know I can make it work by splitting up my query into two queries, like this:
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select r;
var roles2 = from r in roles.AsEnumerable()
select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] };
var list = roles2.ToList();
But I was wondering if there was a more elegant and/or more efficient way to solve this problem, ideally without splitting it in two queries.
Anyway, my question is two parts:
First, can I transform this LINQ query into something that Entity Framework will accept, ideally without splitting into two pieces?
Second, I'd also love to understand a little about EF so I can understand why EF can't layer my custom .NET code on top of the DB access. My DBMS has no idea how to call a method on a Dictionary class, but why can't EF simply make those Dictionary method calls after it's already pulled data from the DB? Sure, if I wanted to compose multiple EF queries together and put custom .NET code in the middle, I'd expect that to fail, but in this case the .NET code is only at the end, so why is this a problem for EF? I assume the answer is something like "that feature didn't make it into EF 1.0" but I am looking for a bit more explanation about why this is hard enough to justify leaving it out of EF 1.0.
The problem is that in using Linq's delayed execution, you really have to decide where you want the processing and what data you want to traverse the pipe to your client application. In the first instance, Linq resolves the expression and pulls all of the role data as a precursor to
New.roles.ToDictionary(row => row.rolename, row => row.roleid);
At that point, the data moves from the DB into the client and is transformed into your dictionary. So far, so good.
The problem is that your second Linq expression is asking Linq to do the transform on the second DB using the dictionary on the DB to do so. In other words, it is trying to figure out a way to pass the entire dictionary structure to the DB so that it can select the correct ID value as part of the delayed execution of the query. I suspect that it would resolve just fine if you altered the second half to
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select r.RoleName;
var list = roles.ToDictionary(roleName => roleName, newRoles[roleName]);
That way, it resolves your select on the DB (selecting just the rolename) as a precursor to processing the ToDictionary call (which it should do on the client as you'd expect). This is essentially exactly what you are doing in your second example because AsEnumerable is pulling the data to the client before using it in the ToList call. You could as easily change it to something like
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select r;
var list = roles.AsEnumerable().Select(r => new Role { Name = r.RoleName, ID = newRoles[r.RoleName] });
and it'd work out the same. The call to AsEnumerable() resolves the query, pulling the data to the client for use in the Select that follows it.
Note that I haven't tested this, but as far as I understand Entity Framework, that's my best explanation for what's going on under the hood.
Jacob is totally right.
You can not transform the desired query without splitting it in two parts, because Entity Framework is unable to translate the get_Item call into the SQL query.
The only way is to write the LINQ to Entities query and then write a LINQ to Objects query to its result, just as Jacob advised.
The problem is Entity-Framework-specific one, it does not arise from our implementation of the Entity Framework support.

Categories