Linq - Selecting or iterating through all grandchildren with VB.NET - linq

All,
I am experimenting with Linq and Entity Framework (athough I am using nHydrate) in VB.NET. Lets say I have 3 tables as follows:
So Table1 is the top level grandparent which has a number of records/entities.
I want to select all of Table3 records/entities that are related to a specific Table1 instance. I want to do this as part of some search functionality.
I want to take an instance of Table1 as my starting point, i.e.
Public Class MySearch
Private _lookUnder As System.Data.Objects.DataClasses.EntityObject
Public Sub Search()
...
CType(_lookUnder, Table1) ' ???? need to linq here ????
...
End Sub
End Class
Can this be done completely with Linq? C# answers are welcome.
I've tried looking at this but didn't help too much.
Thanks,
Andez

Ideally, you should have access to the context, making something like this possible (C#):
var relatedTable3s = context.Table3s.Where(t3 => t3.Table2.Table1.id == _lookUnder.id);
I suppose something like this might work, too:
var relatedTable3s =
from t2 in _lookUnder.Table2s.AsQueryable()
from t3 in t2.Table3s
select t3;
... otherwise written as:
var relatedTable3s = _lookUnder.Table2s.AsQueryable()
.SelectMany(t2 => t2.Table3s);

Related

How join in linq to nhibernate

I have a linq query in Nhibernate.
var q = SessionInstance.Query<Person>();
if (!String.IsNullOrEmpty(dto.FirstName))
q = q.Where(x => x.FirstName.Contains(dto.FirstName));
This query is for Search in persons list. I need to add a join between Person and Employee classes. for add a where condition on a property in Employee class.
For example it :
if (dto.Type == PersonEnumType.EmployeeType)
q = q.Where(employee => employee.Code.Contains(dto.Code));
How can I add something like it?
My sql query is similar this :
select * from Person_Table
left outer join Employee_Table on Person_Table.Id = Employee_Table.Person_id_fk
where Person_Table.FirstName like '%Phill%' and Employee_Table.Code like '332'
It's hard to tell how to do this without knowing your mappings, but it could be something like this:
q.Where(x => x.FirstName.Contains(dto.FirstName))
.Where(x => x.Employees.Any(emp => emp.Code.Contains(dto.Code)))
If there is a Person.Employees at all. But I must admit I don't know if Linq to NHibernate supports Any(). That probably depends on which vesion of L2N you use.
If this does not work you should try your luck with GroupJoin() (because of the outer join), but I'm even more insecure about solid L2N support for that one. As far as I can see it is in L2N since 3.0 beta, but whether it is reliable...?

Optimizing LINQ Any() call in Entity Framework

After profiling my Entity Framework 4.0 based database layer I have found the major performance sinner to be a simple LINQ Any() I use to check if an entity is already existing in the database. The Any() check performs orders of magnitude slower than saving the entity.
There are relatively few rows in the database and the columns being checked are indexed.
I use the following LINQ to check for the existence of a setting group:
from sg in context.SettingGroups
where sg.Group.Equals(settingGroup) && sg.Category.Equals(settingCategory)
select sg).Any()
This generates the following SQL (additionally my SQL profiler claims the query is executed twice):
exec sp_executesql N'SELECT
CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SettingGroups] AS [Extent1]
WHERE ([Extent1].[Group] = #p__linq__0) AND ([Extent1].[Category] = #p__linq__1)
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SettingGroups] AS [Extent2]
WHERE ([Extent2].[Group] = #p__linq__0) AND ([Extent2].[Category] = #p__linq__1)
)) THEN cast(0 as bit) END AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]',N'#p__linq__0 nvarchar(4000),#p__linq__1 nvarchar(4000)',#p__linq__0=N'Cleanup',#p__linq__1=N'Mediator'
Right now I can only think of creating stored procedures to solve this problem, but I would of course prefer to keep the code in LINQ.
Is there a way to make such an "Exist" check run faster with EF?
I should probably mention that I also use self-tracking-entities in an n-tier architecture. In some scenarios the ChangeTracker state for some entities is set to "Added" even though they already exist in the database. This is why I use a check to change the ChangeTracker state accordingly if updating the database caused an insert failure exception.
Try adding index to the database table "SettingGroups", by Group & Category.
BTW, does this produce similar sql?
var ok = context.SettingGroups.Any(sg => sg.Group==settingGroup && sg.Category==settingCategory);
The problem is Entity Framework (at least EF4) is generating stupid SQL. The following code seems to generate decent SQL with minimal pain.
public static class LinqExt
{
public static bool BetterAny<T>( this IQueryable<T> queryable, Expression<Func<T, bool>> predicate)
{
return queryable.Where(predicate).Select(x => (int?)1).FirstOrDefault().HasValue;
}
public static bool BetterAny<T>( this IQueryable<T> queryable)
{
return queryable.Select(x => (int?)1).FirstOrDefault().HasValue;
}
}
Then you can do:
(from sg in context.SettingGroups
where sg.Group.Equals(settingGroup) && sg.Category.Equals(settingCategory)
select sg).BetterAny()
or even:
context.SettingGroups.BetterAny(sg => sg.Group.Equals(settingGroup) && sg.Category.Equals(settingCategory));
I know it sounds a miserable solution, but what happens if you use Count instead of Any?
Have you profiled the time to execute the generated select statement against the time to execute the select what you would expect/like to be produced ? It is possible that it is not as bad as it looks.
The section
SELECT
1 AS [C1]
FROM [dbo].[SettingGroups] AS [Extent1]
WHERE ([Extent1].[Group] = #p__linq__0) AND ([Extent1].[Category] = #p__linq__1)
is probably close to what you would expect to be produced. It is quite possible that the query optimiser will realise the second query is the same as the first and hence it may add very little time to the overall query.

Dynamic Linq Select concatenation

I have a dynamic select statement thus:
"new(PurchaseOrderID as ID_PK, PContractNo + GoodsSupplier.AssociatedTo.DisplayName as Search_Results)"
As can be seen I wish to concatenate the 'PContractNo' and 'GoodsSupplier.AssociatedTo.DisplayName' fields into one returned field named 'Search_Results'. It is important that these two fields are combined.
However the Linq library complains regarding the '+', which the expression parser brings back as a 'Concat(etc...), which of course cannot be translated into a store expression.
Obviously therefore I would like some help regarding just how i should format the select string in order to do what I want. I've tried many things!
Any help would be greatly appreciated!
Thank You, Ian Mac
Create a new Class like
public class A
{
public String k;
public String v;
}
and use linq to join
res = from a in list
select new A
{
k = a.Key,
v = String.Concat(a.Key,a.Value)
};

ef and linq extension method

I have this sql that i want to have written in linq extension method returning an entity from my edm:
SELECT p.[Id],p.[Firstname],p.[Lastname],prt.[AddressId],prt.[Street],prt.[City]
FROM [Person] p
CROSS APPLY (
SELECT TOP(1) pa.[AddressId],a.[ValidFrom],a.[Street],a.[City]
FROM [Person_Addresses] pa
LEFT OUTER JOIN [Addresses] AS a
ON a.[Id] = pa.[AddressId]
WHERE p.[Id] = pa.[PersonId]
ORDER BY a.[ValidFrom] DESC ) prt
Also could this be re-written in linq extension method using 3 joins?
Assuming you have set the Person_Addresses table up as a pure relation table (i.e., with no data besides the foreign keys) this should do the trick:
var persons = model.People
.Select(p => new { p = p, a = p.Addresses.OrderByDescending(a=>a.ValidFrom).First() })
.Select(p => new { p.p.Id, p.p.Firstname, p.p.LastName, AddressId = p.a.Id, p.a.Street, p.a.City });
The first Select() orders the addresses and picks the latest one, and the second one returns an anonymous type with the properties specified in your query.
If you have more data in your relation table you're gonna have to use joins but this way you're free from them. In my opinion, this is more easy to read.
NOTE: You might get an exception if any entry in Persons have no addresses connected to them, although I haven't tried it out.

Linq filter collection with EF

I'm trying to get Entity Framework to select an object and filter its collection at the same time. I have a JobSeries object which has a collection of jobs, what I need to do is select a jobseries by ID and filter all the jobs by SendDate but I can't believe how difficult this simple query is!
This is the basic query which works:
var q = from c in KnowledgeStoreEntities.JobSeries
.Include("Jobs.Company")
.Include("Jobs.Status")
.Include("Category")
.Include("Category1")
where c.Id == jobSeriesId
select c;
Any help would be appreciated, I've been trying to find something in google and what I want to do is here:http://blogs.msdn.com/bethmassi/archive/2009/07/16/filtering-entity-framework-collections-in-master-detail-forms.aspx
It's in VB.NET though and I couldn't convert it to C#.
EDIT: I've tried this now and it doesn't work!:
var q = from c in KnowledgeStoreEntities.JobSeries
.Include("Jobs")
.Include("Jobs.Company")
.Include("Jobs.Status")
.Include("Category")
.Include("Category1")
where (c.Id == jobSeriesId & c.Jobs.Any(J => J.ArtworkId == "13"))
select c;
Thanks
Dan
Include can introduce performance problems. Lazy loading is guaranteed to introduce performance problems. Projection is cheap and easy:
var q = from c in KnowledgeStoreEntities.JobSeries
where c.Id == jobSeriesId
select new
{
SeriesName = c.Name,
Jobs = from j in c.Jobs
where j.SendDate == sendDate
select new
{
Name = j.Name
}
CategoryName = c.Category.Name
};
Obviously, I'm guessing at the names. But note:
Filtering works.
SQL is much simpler.
No untyped strings anywhere.
You always get the data you need, without having to specify it in two places (Include and elsewhere).
No bandwith penalties for retrieving columns you don't need.
Free performance boost in EF 4.
The key is to think in LINQ, rather than in SQL or in materializing entire entities for no good reason as you would with older ORMs.
I've long given up on .Include() and implemented Lazy loading for Entity Framework

Resources