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 }
Related
I'm using sql profiler to see sql generated by Ef core2.1,
this is my linq query :
var resulat = (from a in A
join b in B equals a.level=b.level
where ...
select new M1 {AId = a.id}).Distinct();
(from r in resulat
join c in C equals r.AId = c.AId
select new M2
{
CId = c.Id,
level = _helper(c.level)
}).Distinct();
Sql generated:
select t.AId,c.Id,c.level
from
(
select distinct a.id
from A a
inner join B b on a.level=b.level
where ...
) as t
inner join C c on t.AId = c.AId
What i want as result is :
select distinct c.Id,c.level
from
(
select distinct a.id
from A a
inner join B b on a.level=b.level
where ...
) as t
inner join C c on t.AId = c.AId
I have tried also using select/distinct with result IQueryable, but the sql generated is the same.
what i missed in my linq query or what i have to add to have this sql query
That's what worked for me:
Delete Distinct() from result query, this avoid adding t.AId to my selection.
Delete a helper method from one of my selection fields performe adding Distinct() to final query.
This is my query after correction:
var resulat = from a in A
join b in B equals a.level=b.level
where ...
select new M1 {AId = a.id};
(from r in resulat
join c in C equals r.AId = c.AId
select new M2
{
CId = c.Id
level = c.level
}).Distinct();
Many thanks for your comments, it really helped me.
I'm always a fan of querying the data you want directly from the table (well, DbSet) that returns the data. The process looks a bit like these steps:
I want C.Id and C.Level
That's context.Cs.
Which Cs do I want?
The ones that have a parent A, of which at least one B has the same 'level' as A and meets a couple of other criteria (the where ...).
That amounts to:
from c in context.Cs
where context.Bs.Any(b => b.level == c.A.level && <other criteria>)
select new { c.Id, c.Level }
If the where ... also contains filter criteria for A you can add predicates like && c.A == ... to the where.
Note that I assume a navigation property c.A to be present, otherwise to be created, because C has AId.
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
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
};
}
There are a lot of questions on SO already about Left joins in Linq, and the ones I've looked at all use the join keyword to achieve the desired end.
This does not make sense to me. Let's say I have the tables Customer and Invoice, linked by a foreign key CustomerID on Invoice. Now I want to run a report containing customer info, plus any invoices. SQL would be:
select c.*, i.*
from Customer c
left join Invoice i on c.ID = i.CustomerID
From what I've seen of the answers on SO, people are mostly suggesting:
var q = from c in Customers
join i in Invoices.DefaultIfEmpty() on c.ID equals i.CustomerID
select new { c, i };
I really don't understand how this can be the only way. The relationship between Customer and Invoice is already defined by the LinqToSQL classes; why should I have to repeat it for the join clause? If I wanted an inner join it would simply have been:
var q = from c in Customers
from i in c.Invoices
select new { c, i };
without specifying the joined fields!
I tried:
var q = from c in Customers
from i in c.Invoices.DefaultIfEmpty()
select new { c, i };
but that just gave me the same result as if it were an inner join.
Is there not a better way of doing this?
While the relationship is already defined (both in the database and in the .dbml markup) the runtime cannot automatically determine if it should use that relationship.
What if there are two relationships in the object model (Person has Parents and Children, both relationships to other Person instances). While cases could be special cased, this would make the system more complex (so more bugs). Remember in SQL you would repeat the specification of the relationship.
Remember indexes and keys are an implementation detail and not part of the relational algebra that underlies the relation model.
If you want a LEFT OUTER JOIN then you need to use "into":
from c in Customers
join i in Invoices on i.CustomerId equals c.CustomerId into inv
...
and inv will have type IEnumerable<Invoivce>, possibly with no instances.
What are you talking about? That from i in c.Invoice.DefaultIfEmpty() is exactly a left join.
List<string> strings = new List<string>() { "Foo", "" };
var q = from s in strings
from c in s.DefaultIfEmpty()
select new { s, c };
foreach (var x in q)
{
Console.WriteLine("ValueOfStringIs|{0}| ValueOfCharIs|{1}|",
x.s,
(int)x.c);
}
This test produces:
ValueOfStringIs|Foo| ValueOfCharIs|70|
ValueOfStringIs|Foo| ValueOfCharIs|111|
ValueOfStringIs|Foo| ValueOfCharIs|111|
ValueOfStringIs|| ValueOfCharIs|0|
You may probably want to use the 'into' keyword.
Example
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
};