Eager loading with condition - linq

I have question on eager loading in entity framework.
I have two tables ScrappyTemplate and ScarppyTemplateFields, the relationship between the tables is one to many. Note that both the tables have IsActive flag
I want to fetch the data from ScrappyTemplate and ScrappyTemplateFields where IsActive==True, im using the below code to fetch the data via eager loading
using (Entities entities = new Entities())
{
var content = entities.ScrappyTemplates.Include(entities.GetTableName<ScrappyTemplateField> (false)).Where(c => c.ContentSourceId == contentSourceId && c.IsActive == true && c.ScrappyTemplateFields.Any(d=>d.IsActive==true)).ToList<ScrappyTemplate>();
}
Im getting the resultset, which is not right!!, i want to get the result set of Child table i.e ScrappyTemplateFields where IsActive=true, but it is returning all rows ir-respective IsActive flag.
Please any one help me, how to place a condition in Child table.
Thanks in advance

.Include() does not allow filtering on the related entities. Try this:
using (Entities entities = new Entities())
{
var query = from template in entities.ScrappyTemplates
where template.ContentSourceId = contentSourceId && template.IsActive = true && template.ScrappyTemplateFields.Any(d=>d.IsActive==true)
select new {
Template = template,
TemplateFields = template.ScrappyTemplateFields.Where(d=>d.IsActive==true)
};
var content = query.ToList().Select(t=>t.Template);
}

Related

Entity Framework Core - Upsert entities from other database encounters tracking problems

I have a flatfile from a different database. I import it and map it to my application's entities. Because the flatfile does not contain ids I cannot be sure the entries I handle are not duplicates of what has already been added to my database earlier or to my context at this moment.
The error message I get is:
The instance of entity type 'Car' cannot be tracked because another
instance with the same key value for {'Make', 'Model'} is already
being tracked. When attaching existing entities, ensure that only one
entity instance with a given key value is attached. Consider using
'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the
conflicting key values.
An example:
Data rows from flatfile
Volvo V70 Steve
Volvo V70 John
Having mapped these rows and trying to put them in db
foreach(var row in flatFileRows){
Car existingCar = null;
if(dbContext.Cars.Any(c => c.Make == row.Make && c.Model == row.Model)){
existingCar = dbContext.Cars
.SingleOrDefault(c => c.Make == row.Make && c.Model == row.Model);
}
//I also do the same for existingDriver
var car = existingCar != null
? existingCar
: new Car()
{
Make = row.Make,
Model = row.Model,
Drivers = new List<Driver>();
};
var driver = new Driver()
{
CarId = existingCar != null ? exsitingCar.Id : 0,
Name = row.Name
};
car.Drivers.Add(driver);
dbContext.Cars.Update(car); //Second time we hit this the error is thrown
}
dbContext.SaveChanges();
Make and Model are set to keys in the schema because I don't want duplicate entries of the car models.
The above example is simplified.
What I want is to check if I already put a car in the db with these attributes and then build according to my schema from that entity. I don't care to track any entries, disconnected or otherwise, because I just need to populate the database.

CRM Linq find all parents that have 0 children

How can I find (preferably using CRM Linq) parent entities that have 0 children. For example how can I find all accounts that have 0 contacts.
If you are going to use the query expression route, which I would recommend then the following code will be useful
var entityAlias = "con";
var query = new QueryExpression
{
EntityName = "account",
ColumnSet = new ColumnSet(true),
Criteria =
{
FilterOperator = LogicalOperator.And,
Conditions =
{
new ConditionExpression(entityAlias, "contactid",ConditionOperator.Null)
}
}
LinkEntities =
{
new LinkEntity
{
EntityAlias = entityAlias,
LinkFromEntityName = "account",
LinkFromAttributeName = "accountid",
LinkToEntityName = "contact",
LinkToAttributeName = "parentcustomerid",
Columns = new ColumnSet("parentcustomerid", "contactid"),
JoinOperator = JoinOperator.LeftOuter,
}
},
};
var response = service.RetrieveMultiple(query);
var accounts = response.Entities;
In this code I have not limited the columns, this will reduce performance and you should only return the columns needed.
If there is the case for more than 5000 records are going to be returned then you will need to use paging and loop the query to find all the entities,
This can be found here:
https://msdn.microsoft.com/en-us/library/gg327917.aspx
However if you are certain you want to use LINQ then you can use the following code:
public static IEnumerable<Account> FindAccountsWithNoContacts()
{
var contactRelationship = new Relationship("contact_customer_accounts");
foreach(var account in XrmContext.AccountSet)
{
XrmContext.LoadProperty(contactRelationship);
if(!account.RelatedEntities.ContainsKey(contactRelationship)
yield return account;
}
}
My problem with the LINQ code is that all the enities, both the account and contact entities, will be loaded into memory. With large entity sets this can cause OutOfMemoryException, whereas the query expression route will pass the query to the Dynamics server to execute; which should make the execution of the code faster.
The thing you are looking for is left outer join. Which is unfortunately not possible in CRM using LINQ. However you can do it using query expression or FetchXML.
Here is a link that can help you:
https://community.dynamics.com/crm/b/gonzaloruiz/archive/2014/02/23/all-about-outer-join-queries-in-crm-2011-and-crm-2013

UpdateRelatedObject method only works when the sourceProperty is not collection

I am getting the following error when I try to update tree of object using asp.net webapi OData:
"UpdateRelatedObject method only works when the sourceProperty is not collection."
My code is provided below. I got this error when the mehod "UpdateRelatedObject" is called. Can you please advise what is wrong with my code and how to update tree of objects (meaning object contains collection of child objects) using asp.net webapi odata v4.
var container = new Container(new Uri("http://JohnAlbert.com/MyOdataTest/odata"));
Product product = container.Products.Expand(p=> p.ProductItems).Expand(p=>p.ProductInvoices).Where(p => p.PId == Guid.Parse("28C508B8-F2DC-45C2-B401-7F94E79AB347")).FirstOrDefault();
if (product != null)
{
product.Name = product.Name + "_Modified";
var pitem1 = product.ProductItems[0];
product.ProductItems.Remove(pitem1);
container.UpdateRelatedObject(product, "ProductItems", pitem1);
var pitem2 = product.ProductItems[0];
pitem2.Price = 999;
container.UpdateRelatedObject(product, "ProductItems", pitem1);
var pInv1 = product.ProductInvoices[0];
product.ProductInvoices.Remove(pInv1);
container.UpdateRelatedObject(product, "ProductInvoices", pInv1);
}
container.UpdateObject(product);
container.SaveChanges(SaveChangesOptions.BatchWithSingleChangeset);
What you actually want to delete the relationship between some items in a collection-valued navigation property of an entity and itself. In such case DeleteLink() is the right method to use. In this case the following code should do the work:
if (product != null)
{
var pitem1 = product.ProductItems[0];
var pitem2 = product.ProductItems[0];
var pInv1 = product.ProductInvoices[0];
container.DeleteLink(product, "ProductItems", pitem1);
container.DeleteLink(product, "ProductItems", pitem2);
container.DeleteLink(product, "ProductInvoices", pInv1);
container.SaveChanges();
}
You may think the above way isn't as intuitive as directly removing the navigation items from the entity using .Remove() as you did. For this problem, the entity tracking enabled by using DataServiceCollection<T> can help. You can use this blog post as a tutorial for how to use DataServiceCollection<T>: http://blogs.msdn.com/b/odatateam/archive/2014/04/10/client-property-tracking-for-patch.aspx
To delete a contained navigation property, you can use DataServiceContext.DeleteObject.
To delete a relationship between an entity and its navigation property, you can use DataServiceContext.DeleteLink
To update an object, you can use DataServiceContext.UpdateObject.
So according to your scenario, you could use following code
if (product != null)
{
product.Name = product.Name + "_Modified";
dsc.UpdateObject(product);
var pitem1 = product.ProductItems[0];
//This is to remove relationship
container.DeleteLink(product, "ProductItems", pitem1);
// This is to remove the object
//container.DeleteObject(pitem1);
var pitem2 = product.ProductItems[0];
pitem2.Price = 999;
container.UpdateObject(pitem2);
var pInv1 = product.ProductInvoices[0];
//This is to remove relationship
container.DeleteLink(product, "ProductInvoices", pInv1);
// This is to remove the object
//container.DeleteObject(pInv1);
container.SaveChanges(SaveChangesOptions.BatchWithSingleChangeset);
}

LINQ to Entities does not recognize the method 'Boolean CheckMeetingSettings(Int64, Int64)' method

I am working with code first approach in EDM and facing an error for which I can't the solution.Pls help me
LINQ to Entities does not recognize the method 'Boolean
CheckMeetingSettings(Int64, Int64)' method, and this method cannot be
translated into a store expression.
My code is following(this is the query which I have written
from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
}
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
Please help me out of this.
EF can not convert custom code to SQL. Try iterating the result set and assigning the property outside the LINQ query.
var people = (from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
order by /**/
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
}).Skip(/*records count to skip*/)
.Take(/*records count to retrieve*/)
.ToList();
people.ForEach(p => p.CanSendMeetingRequest = CheckMeetingSettings(6327, p.Id));
With Entity Framework, you cannot mix code that runs on the database server with code that runs inside the application. The only way you could write a query like this, is if you defined a function inside SQL Server to implement the code that you've written.
More information on how to expose that function to LINQ to Entities can be found here.
Alternatively, you would have to call CheckMeetingSettings outside the initial query, as Eranga demonstrated.
Try:
var personDetails = obj.tempPersonConferenceDbSet.Where(p=>p.ConferenceId == 2).AsEnumerable().Select(p=> new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
});
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
You must use AsEnumerable() so you can preform CheckMeetingSettings.
Linq to Entities can't translate your custom code into a SQL query.
You might consider first selecting only the database columns, then add a .ToList() to force the query to resolve. After you have those results you van do another select where you add the information from your CheckMeetingSettings method.
I'm more comfortable with the fluid syntax so I've used that in the following example.
var query = obj.tempPersonConferenceDbSet
.Where(per => per.Conference.Id == 2).Select(per => new { Id = per.Person.Id, JobTitle = per.Person.JobTitle })
.ToList()
.Select(per => new PersonDetails { Id = per.Id,
JobTitle = per.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327, per.Person.Id) })
If your CheckMeetingSettings method also accesses the database you might want to consider not using a seperate method to prevent a SELECT N+1 scenario and try to express the logic as part of the query in terms that the database can understand.

Linq: How to load a second table directly?

I have 2 tables here:
Auction and Article.
1 Auction has 1 Article.
1 Aricle has x Auctions.
Now I load a list of auctions:
using (APlattformDatabaseDataContext dc = new APlattformDatabaseDataContext())
{
List<Auktion> auctionlist = (from a in dc.Auktion
where a.KäuferID == null
select a).ToList();
return auctionlist;
}
But when I want to "txtBla.Text = auction[0].Article.Text;" it isn't loaded.
The question isn't why (it is logic that it isn't loaded allready and can't be loaded because the DC is closed), but how can I solve this without letting the DC open?
You can do the following:
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Auktion>(a => a.Article);
dc.LoadOptions = options;
If you want to eager load the associations like that you should use the DataContext.LoadOptions property like so...
using (APlattformDatabaseDataContext dc = new APlattformDatabaseDataContext())
{
var dlo = new DataLoadOptions();
options.LoadWith<Auktion>(o => o.Article);
dc.LoadOptions = dlo;
List<Auktion> auctionlist = (from a in dc.Auktion
where a.KäuferID == null
select a).ToList();
return auctionlist;
}
That way your articles will be loaded when your Auktion objects are retrieved from the database.

Resources