Subquery nightmares in EF - linq

I'm really, really struggling with what should otherwise be a straightforward query in anything other than LINQ (for example SQL!)
I have two entities:
Product
ProductApprover
The Product entity has a one to many relationship on the ProductApprover entity, e.g:
Product.ProductApprovers gives me all ProductApprover entities relating to the Product.
Getting a Product and associated ProductApprover data is simple enough when querying by my ProductID column on my Product entity as the ProductApprover data associated is bundled into the result automatically, but my problem comes when I want to alter my query by querying data WITHIN my associated ProductApprover entities. I have tried all sorts with use of the 'Where', 'Contains' and 'Any', functions, etc, to perform a subquery, but cannot seem to get the result I want.
The query I want to perform is:
SELECT * FROM Product p
INNER JOIN ProductApprover pa ON p.ProductId = pa.ProductId
WHERE p.ProductId = #id AND pa.Version = #version
Can anybody help me out please? Thank you kindly in advance.

Try this (I guess this is a LINQ interpretation of your SQL query):
int id = 123;
int version = 555;
var results = from p in context.Products
join pa in context.ProductApprovers
on p.ProductId = pa.ProductId
where p.ProductId equals id && pa.Version equals version
select new { Product = p, Approver = pa };

I suspect you want something like this:
var query = from product in db.Products
where product.ProductId == productId
select new {
Product = product,
Approvers = product.Approvers.Where(pa => pa.Version == version)
};
If that doesn't do what you want, could you explain where it falls down?

Related

How to retieve CRM Guid using LINQ and joins?

We are using CRM 2011. We have Contracts with Entity Reference to Product and each Product has an Entity Reference to a Subject. Given a Contract Guid, I need to retrieve the Subject Guid.
I am a beginner at LINQ but I coded:
var subject = from s in context.SubjectSet
join product in context.ProductSet
on s.Id equals product.SubjectId.Id
join contract in context.ContractSet
on product.Id equals contract.ce_ProductId.Id
where contract.Id == gContractId
select s;
foreach (var s in subject)
{
newReportableAction.ce_SupergroupRegarding =
new EntityReference(Xrm.Subject.EntityLogicalName, new Guid(s.Id.ToString()));
}
This throws an error:
AttributeFrom and AttributeTo must be either both specified or both ommited. You can not pass only one or the other. AttributeFrom: , AttributeTo: ce_ProductId
What does this error mean?
How can I get the Guid?
Update:
I tried breaking the query into parts to see where the error was being generated from so I had:
var query = from product in context.ProductSet
join contract in context.ContractSet
on product.Id equals contract.ce_ProductId.Id
This gives:
"The type of one of expressions in the join clause is incorrect. Type inference failed in the call to 'Join'"
Thank you to all who help...
I don't think you can use the .Id property right off the entity in Linq statements. Try this instead:
var query = from product in context.ProductSet
join contract in context.ContractSet
on product.ProductId equals contract.ce_ProductId.Id
Notice product.ProductId instead of product.Id.

Entity Framework returns wrong data after execution of two similar queries

I have two similar queries, the first one:
var activatedSerialNumbers = (from activation in entities.Activations
where !canceledActivationsIds.Contains(activation.Id)
where activation.CustomerId == customerId
join licenseConfiguration in entities.LicenseConfigurations
on activation.Id equals licenseConfiguration.ActivationId
where licenseConfiguration.ProductId == productId
join activatedSerialNumber in entities.ActivatedSerialNumbers
on activation.Id equals activatedSerialNumber.ActivationId
where deactivatedSams.All(dsn => dsn.ToLower() !=
activatedSerialNumber.Name.ToLower())
select new SamWithLicense
{
Name = activatedSerialNumber.Name,
Features = licenseConfiguration.LicenseFeatures
}).ToList();
The second:
var activationsForSam = (from activation in entities.Activations
where !canceledActivationsIds.Contains(activation.Id)
where activation.CustomerId == customerId
let activatedSerialNumbers = activation.ActivatedSerialNumbers
.Select(sn => sn.Name.ToLower())
where activatedSerialNumbers.Contains(loweredSn)
join licenseConfiguration in entities.LicenseConfigurations
on activation.Id equals activatedProduct.ActivationId
select new SamWithLicense
{
Name = selectedSerialNumber,
Features = licenseConfiguration.LicenseFeatures
}).ToList();
In some situations I execute them one after another and in most cases it works fine, but somethimes - not. In the result of second query Counter takes from another row:
Visual Studio - Quick watch
SQL Management Studio
I guess it's a matter of a EF cache or smth, but don't know how to fix it properly.
In your first query you are joining the Activation Id (PK) to LicenseConfigurations ActivationId (FK)
join licenseConfiguration in entities.LicenseConfigurations
on activation.Id equals licenseConfiguration.ActivationId
in your second query, it looks like you are joining on a value defined outside of the query "activatedProduct"
join licenseConfiguration in entities.LicenseConfigurations
on activation.Id equals activatedProduct.ActivationId

Linq Outer Joins - don't want DefaultIfEmpty, rather want NullIfEmpty

all,
This problem relates to the Dynamics CRM 2011 Linq provider - which does have a LOT of quirks. However, I have not tagged it such as I think this is a general Linq question.
I have a class- Product. It has a property (say ProductPrice) of type Price.
I am doing an Outer Join on this in Linq. The CRM documentation says outer joins are not possible, but it seems to work (with the obvious problem I am asking here).
So say I am doing something like: (apologies for the pseudo linq)
IList<Product> products = (from p in xrmContext.Products
join pr in xrmContext.Prices
on p.ProductId equals pr.ProductId into prx from prices in prx.DefaultIfEmpty
select new Product { ProductName = p.productName, ProductPrice = new Price { Amount = prices.PriceValue }).ToList();
This works great to a point. It creates all the products irrespective of whether they have a price object or not. Tippety top.
The problem is the DefaultIfEmpty. As you are no doubt aware if a product has no price this DefaultIfEmpty will create a 'default' price object ... i.e. an object with null values. What I actually want is NO price object - i.e. null, not a 'blank' object.
How is that possible?
I have worked round it by testing for a blank price name - ProductPrice = price.priceName == "" ? null : new Price ...
It would be nice to be able to do something like NullIfEmpty. Any ideas?
You can skip the join:
from p in xrmContext.Products
let price = xrmContext.Prices.FirstOrDefault(pr => pr.ProductID == p.ProductID)
select new Product()
{
ProductName = p.productName,
ProductPrice = price != null ? new Price() { Amount = price.PriceValue } : null
}
crm 2011 linq doesn't support outer join.
If you try Amiram's code - when evaluating the select, you'll get an error "products doesn't contain attribute "pricevalue"".

LINQ/LinqPad: same query different results

So we copy and paste the exact same query from LinqPad into our EF 4.3 application, pointed at the exact same database and get a different result. In LinqPad we get 2 records returned. In our application we reaise an error "Object reference not set to an instance of an object."
var Shippings = shippingRepository.All.ToArray();
var SalesOrderHeaders = salesOrderHeaderRepository.All.ToArray();
var Customers = customerRepository.All.ToArray();
var Stores = storeRepository.All.ToArray();
var Departments = departmentRepository.All.ToArray();
var toShip = from sh in Shippings
join h in SalesOrderHeaders on sh.OrderId equals h.SalesOrderHeaderId
join c in Customers on h.CustomerId equals c.CustomerId
join st in Stores on h.StoreId equals st.StoreId
join d in Departments on h.DepartmentId equals d.DepartmentId into outer
from o in outer.DefaultIfEmpty()
select new
{
OrderId = sh.OrderId,
CustomerName = c.Name,
StoreName = st.Name,
DepartmentName = (o.Name == null) ? o.Name : "None",
DeliveryDate = h.DeliveryDateTime
};
In the application code, when we remove the outer join (to add Departments) and it's associated field the query returns the same 2 records asn in LinqPad.
Does anyone have any insight into how to fix this feature?
Click on "Add a connection" in linqpad and select datacontext from assembly like
You can choose Entity Framework datacontext or Entity Framework BDContext with POCO depending upon your scenario. click next and provide path to the assembly along with connection string and you will be good to go.
In LINQPad are you actually querying against your entity model? Take a look at this link if you aren't. I had a similar problem when starting out and didn't realize I had set up a default LINQ to SQL connection earlier and was querying against that.

Linq to SQL - Many to Many Predicates

I'm familiar with doing simple Many-to-Many relationships (i.e. simple joins) in Linq to SQL, but I'm having a hard time thinking right now.
I have three tables (and so, entities in my Linq-to-SQL model) representing a taxonomic system. Standard-issue really:
Products - ProductTags - Tags
I'm writing a method that returns a set of Products where the Tag they're in matches a query. So if someone searches for "foo" then all products assigned with the tags "foobar" or "fooqux" (but not "bazbar") would be returned.
I know I have to structure the query into two parts: first to get the matching Tags, and then to get the Products that have those tags. It's the second part I'm stumped on.
Here's what I've got so far:
var tags = from t in db.Tags
where t.Name.Contains( tagSearchQuery )
select t;
var products = from p in db.Products
// then a miracle happens
select p;
Assistance much appreciated :)
You can do it in one query if you just start with the ProductTags table. You'll probably also need a Distinct to avoid duplicate products matching multiple tags.
var products = (from pt in db.ProductTags
where pt.Tag.Name.Contains( tagSearchQuery )
select pt.Product).Distinct();
or here's another way:
var products = from p in db.Products
from pt in p.ProductTags
where pt.Tag.Name.Contains( tagSearchQuery )
select p
IQueryable<Tag> tags =
from t in db.Tags
where t.Name.Contains( tagSearchQuery )
select t;
IQueryable<Product> products =
from p in db.Products
where p.ProductTags.Any(pt => tags.Contains(pt.Tag))
select p;
OR
IQueryable<Product> products =
from p in db.Products
from pt in p.ProductTags
let t = pt.Tag
where t.Name.Contains( tagSearchFragment )
group t by p into g
select g.Key;

Resources