LinqToSql query that spans a many-to-many relation? - c#

Let say i have the following schema
Content(Id, ....)
TagContent(TagId, ContentId)
Tag(TagId, Name)
Suppose I'd like to select all content records that have tag with name "test".
In SQL I would write:
select Content.Id
from Content
join TagContent as TC on (TC.ContentId = Content.Id)
Join Tag on (TC.TagId = Tag.Id)
where Tag.Name = 'Test'
Could you suggest how to write a similar query in Linq if you have only Table available?
(I'd like to create an extension method Content.ByTag('tag') -> IQueryable )
I've only managed to create a query that use the sql exists statement instead of join.
Which means that the queries are extremely inefficient.
My current inefficient solution looks as follows:
DataContext.Contents.Where(c => c.TagContents.Any(tc => tc.Tag.Name == "Test"))
NOTE:
As I'd like to make the extension method on DataContext.Contents I won't have access to other tables that is DataContext.Tag and DataContext.ContentTag.

Something like this perhaps
var contentIds = from c in Content
join tc in TagContent on c.Id equals tc.ContentId
join t in Tag on tc.TagId equals t.Id
where t.Name == "Test"
select new
{
ContentId = c.Id
};

Related

Rewrite Entity Framework LINQ query into T-SQL

How can I rewrite the following Linq query into normal T-SQL?
var list = (from t in context.ParentTable
where t.ChildRecords.Count == t.ChildRecord.Count( c => c.BooleanColumn )
select t).ToList();
Thanks in advance...
Something like this, but you'll need to determine the relationship between the ParentTable and ChildRecord tables to make it work, I'm just guessing at the cr.ParentTableId = pt.ParentTableId part.
select pt.*
from ParentTable pt
where not exists
(select 1
from ChildRecord cr
where cr.ParentTableId = pt.ParentTableId
and cr.BooleanColumn = 0)
On a side note the Linq could be changed to the following instead.
var list = (from t in context.ParentTable
where t.ChildRecords.All(c => c.BooleanColumn)
select t).ToList();

"Invalid Where condition" when I try to search for a value that is accented ignoring accents in CRM 2011 with LINQ

I 'm trying to do a search for a contact. For example value "Café " which is stored in the name field , but when I search like "cafe" does not return any record .
I tried to do the following
using (ServiceContext svcContext = new ServiceContext(_serviceProxy))
{
var query_where3 = from c in svcContext.ContactSet
join a in svcContext.AccountSet
on c.ContactId equals a.PrimaryContactId.Id
where c.FullName.Normalize(NormalizationForm.FormD).Contains("Café")
select new
{
account_name = a.Name,
contact_name = c.LastName
};
}
and appear the Exception with message saying "Invalid 'where' condition. An entity member is invoking an invalid property or method"
You can't use that functions on LinQ-CRM, the correct way to do the query is:
c.FullName == "someString" or c.FullName.equals("someString").
This is because you can't use functions or transformations on the left condition. You must use the attribute itself.
Your query will look like:
using (ServiceContext svcContext = new ServiceContext(_serviceProxy))
{
var query_where3 = from c in svcContext.ContactSet
join a in svcContext.AccountSet
on c.ContactId equals a.PrimaryContactId.Id
where c.FullName == "Café" || c.FullName == "Cafe"
select new
{
account_name = a.Name,
contact_name = c.LastName
};
}
You can't really deal with the accents with Linq to SQL in general ... and you are even more limited with what you can do with Linq to CRM. You cant modify the DB; unless you don't care about being supported. Then you could do something like : MAD suggested and to a db alter.
ALTER TABLE Name ALTER COLUMN Name [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AI
I personally would not recommend that.
The best that I can come up with is getting the data as close as you can and filtering it from there inside a list or something similar.
I have to do it all the time and it is a pain (and adds more overhead) but there is not really another workaround that I have found.
//declare a dictionary
Dictionary<string, string> someDictionary = new Dictionary<string, string> ();
using (ServiceContext svcContext = new ServiceContext(_serviceProxy))
{
var query_where3 = from c in svcContext.ContactSet
join a in svcContext.AccountSet
on c.ContactId equals a.PrimaryContactId.Id
where c.FullName.Contains("Caf")
select new
{
account_name = a.Name,
contact_name = c.LastName
};
}
//then
foreach(var q in query_where3)
{
if(string.IsNullOrEmpty(account_name)==false && string.IsNullOrEmpty(contact_name)==false)
{
someDictionary.Add(account_name, contact_name);
}
}
//then you can add the .Normalize(NormalizationForm.FormD) to your dictionary
Hope that helped.
Its all about
.Normalize(NormalizationForm.FormD)
, probably EF does not knows how to handle this method. Remove it and test just with
c.FullName.Contains("Café")
------------------------------------------------- Added in 2015-01-30 --------------------------------------------------
So man, the unique solution i can think about is list before you do the where condition. This way the you can use the normalize once this will be handled by linq 2 objects. try:
(from c in svcContext.ContactSet join a in svcContext.AccountSet
on c.ContactId equals a.PrimaryContactId.Id
select new {a=a,c=c} ).ToList()
.Where(c=>c.FullName.Normalize(NormalizationForm.FormD).Contains("Café"))
.Select( x=> select new {
account_name = x.a.Name,
contact_name = x.c.LastName
};)
But that way can cause some overhead given that linq 2 obejects runs in application server memory, not in database server.
CRM's LINQ translator cannot handle the .Equals() method.
on c.ContactId equals a.PrimaryContactId.Id
Change the above line to below line.
on c.ContactId == a.PrimaryContactId.Id

Simple Linqpad Linq join statement, select all fields

I'm trying to do a simple join query in Linqpad, to mimic the following ...
SELECT *
FROM Companies C
JOIN Addresses A
ON A.CompanyID = C.CompanyID
WHERE C.CompanyID = 123
I'm using C# Expression (so no need to Dump, AFAIK), and tried the following unsuccessfully ...
from C in Companies
join A in Addresses on C.CompanyID equals A.CompanyID
where C.CompanyID = 123
select C,A
So the result is "name does not exist in current context"
I suspect it might be because of common field in both tables, or the C,A syntax is basically wrong.
I want to be able to do the splat "*" across both tables. Is this possible?
I believe your select should look like
select new { C, A }
or
select new { Company = C, Address = A }
as you are returning more than one complex object, so it should be wrapped with an anonymous type. Or you can define your type and then set the properties like so
select new MyType { Company = C, Address = A }

Dynamic Join of multiple entities based on some filter in Entity Framework

I am pretty new to Entity Framework and LINQ and I have an entity with more than 10+ other associated entities (one-to-many relationships). Now, I'm planning to make a search page in my application in which users could select which fields (i.e. those 10+ tables) they want to be considered when searching.
Now, I'm trying to write a query to achieve the above goal. Any help how I could sort this out using LINQ method syntax? I mean, to write a multiple join query based on user's choice. (i.e. which of Class1, Class2, ... to join with main Entity to finally have all the related fields in one place). Below is a sample code (Just a hunch, in fact)
if(somefilter#1)
result = db.Companies.Join(db.Channels, p => p.Id, k => k.CId,
(p, k) => new {Company = p, Channels=k});
if(somefilter#2)
result = result.Join(db.BusinnessType, ........);
if(somefilter#3)
result = result.Join(db.Values, .......);
For complex queries it may be easier to use the other LINQ notation. You could join multiple entities like this:
from myEntity in dbContext.MyEntities
join myOtherEntity in dbContext.MyOtherEntities on myEntity.Id equals myOtherEntity.MyEntityId
join oneMoreEntity in dbContext.OneMoreEntities on myEntity.Id equals oneMoreEntity.MyEntityId
select new {
myEntity.Id,
myEntity.Name,
myOtherEntity.OtherProperty,
oneMoreEntity.OneMoreProperty
}
You can join in other entities by adding more join statements.
You can select properties of any entity from your query. The example I provided uses a dynamic class, but you can also define a class (like MyJoinedEntity) into which you can select instead. To do it you would use something like:
...
select new MyJoinedEntity {
Id = myEntity.Id,
Name = myEntity.Name,
OtherProperty = myOtherEntity.OtherProperty,
OneMoreProperty = oneMoreEntity.OneMoreProperty
}
EDIT:
In case when you want to have conditional joins you can define MyJoinedEntity with all the properties you will need if you were to join everything. Then break up the join into multiple methods. Like this:
public IEnumerable<MyJoinedEntity> GetEntities() {
var joinedEntities = from myEntity in dbContext.MyEntities
join myOtherEntity in dbContext.MyOtherEntities on myEntity.Id equals myOtherEntity.MyEntityId
join oneMoreEntity in dbContext.OneMoreEntities on myEntity.Id equals oneMoreEntity.MyEntityId
select new MyJoinedEntity {
Id = myEntity.Id,
Name = myEntity.Name,
OtherProperty = myOtherEntity.OtherProperty,
OneMoreProperty = oneMoreEntity.OneMoreProperty
};
if (condition1) {
joinedEntities = JoinWithRelated(joinedEntities);
}
}
public IEnumerable<MyJoinedEntity> JoinWithRelated(IEnumerable<MyJoinedEntity> joinedEntities) {
return from joinedEntity in joinedEntities
join relatedEntity in dbContext.RelatedEntities on joinedEntity.Id equals relatedEntity.MyEntityId
select new MyJoinedEntity(joinedEntity) {
Comments = relatedEntity.Comments
};
}

Newbie on linq to sql lambda expression where statement after two includes

var Query2 = from x in CoreDatabase.Set<tblPerson>()
.Include(a => a.tblApplicationInterface.Select(b => b.tblApplicationName)
.Where(c => c.AppplicationName == "MshHumanResources"))
select x;
I get The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
I am simply trying to write
Here something that works:
Select *
From tblPerson a INNER JOIN tblApplicationInterface b
on a.id = b.id
INNER Join tblApplicationName c
ON b.fkid=c.id
Where b.ApplicationName like 'MshHumanResources'
That's not how Include works. You want to use join instead:
var Query2 = from a in CoreDatabase.Set<tblPerson>()
join b in CoreDatabase.Set<tblApplicationInterface>() on a.id equlas b.id
join c in CoreDatabase.Set<tblApplicationName>() on b.fkid equals c.id
where c.AppplicationName == "MshHumanResources"
select a;
This is just selecting the columns from A - if you want to select columns from the other tables either create a class to combine the fields you need or use an anonymous type.
Directly, you could say
var people = CoreDatabase.Set<tblPerson>().Where( p =>
p.tblApplicationInterface.tblApplicationName.ApplicationName == "MshHumanResources" );
If I understand your navigation properties correctly. This would select people who's app interface's app name was MshHumanResources.
Try this:
var Query2 = CoreDatabase.Set<tblPerson>
.Include("tblApplicationInterface")
.Include("tblApplicationInterface.tblApplicationName")
.Where(x => x.ApplicationName == "MshHumanResources");

Categories

Resources