Entity Framework Join multiple tables is slow - sql

first time I was trying to join this many tables using Entity Framework.
Everything works well but loading time is about 1 minute, very bad. I dont know if is some different solution to join this many tables, or something what could help.
It is about 5000 records in database. I think in future number of this records can be more higher. I need something what will take it. Thanks.
Code below:
var JoinedTopCars = db.CustomerAdvertisement.Where(o => o.Top == true)
.Join(
db.CarManufacturerName,
n => n.CarManufacturerId,
q => q.CarManufacturerId,
(n, q) => new
{
n,
q,
})
.Join(
db.CarManufacturerModel,
o => o.n.CarModelId,
j => j.CarModelId,
(o, j) => new
{
j,
o,
}).Where(y => y.j.CarManufacturerId == y.o.q.CarManufacturerId)
.Join(
db.TypeOFFuel,
e => e.o.n.FuelId,
r => r.Id,
(e, r) => new
{
e,
r,
})
.Join(
db.TypeOFGearBox,
t => t.e.o.n.GearBoxId,
i => i.Id,
(t, i) => new
{
t,
i,
})
.Join(
db.Country,
y => y.t.e.o.n.CountryOfOrigin,
u => u.Id,
(y, u) => new
{
y,
u,
})
.Join(
db.TypeOFChassis,
c => c.y.t.e.o.n.ChassisId,
d => d.Id,
(c, d) => new
{
c,
d,
})
.Join(
db.CarDoors,
e => e.c.y.t.e.o.n.CarDoorsId,
f => f.Id,
(e, f) => new
{
e,
f,
})
.Join(
db.TypOfMovement,
g => g.e.c.y.t.e.o.n.MovementId,
f => f.Id,
(g, f) => new
{
g,
f,
})
.Join(
db.Area,
i => i.g.e.c.y.t.e.o.n.AreaOfOrigin ?? 0,
f => f.Id,
(i, f) => new
{
i,
f,
})
.Join(
db.District,
j => j.i.g.e.c.y.t.e.o.n.OkresOfOrigin ?? 0,
f => f.Id,
(j, f) => new
{
j,
f,
})
.Join(
db.CarColor,
k => k.j.i.g.e.c.y.t.e.o.n.CarColorId,
x => x.Id,
(k, x) => new JoinedTopCars
{
Id = k.j.i.g.e.c.y.t.e.o.n.Id,
Objem = k.j.i.g.e.c.y.t.e.o.n.cm3,
Carname = k.j.i.g.e.c.y.t.e.o.q.CarName,
CarModel = k.j.i.g.e.c.y.t.e.j.CarModel,
Typ = k.j.i.g.e.c.y.t.e.o.n.ModelType,
Color = x.ColorName,
Karoseria = k.j.i.g.e.d.ChassisName,
Dvere = k.j.i.g.f.NumberOfDoors,
Pohon = k.j.i.f.Movement,
VIN = k.j.i.g.e.c.y.t.e.o.n.VIN,
Metalic = k.j.i.g.e.c.y.t.e.o.n.Metalic,
Poskodene = k.j.i.g.e.c.y.t.e.o.n.Crashed,
Pojazdne = k.j.i.g.e.c.y.t.e.o.n.Drivable,
DPH = k.j.i.g.e.c.y.t.e.o.n.DPH,
Leasing = k.j.i.g.e.c.y.t.e.o.n.Leasing,
Emisie = k.j.i.g.e.c.y.t.e.o.n.Emmisions,
Spotreba = k.j.i.g.e.c.y.t.e.o.n.Consumption,
Km = k.j.i.g.e.c.y.t.e.o.n.KM,
Rok = k.j.i.g.e.c.y.t.e.o.n.DateOfOrigin.ToString(),
Vykon = k.j.i.g.e.c.y.t.e.o.n.HP,
Palivo = k.j.i.g.e.c.y.t.r.Fuel,
Prevodovka = k.j.i.g.e.c.y.i.GearBox,
Krajina = k.j.i.g.e.c.u.CountryName,
Okres = k.f.DistrictName,
Kraj = k.j.f.AreaName,
Vybava = k.j.i.g.e.c.y.t.e.o.n.Equipment,
Popis = k.j.i.g.e.c.y.t.e.o.n.Description,
Kontakt = k.j.i.g.e.c.y.t.e.o.n.ContInfo,
ZobrazMeno = k.j.i.g.e.c.y.t.e.o.n.ShowName,
ZobrazCislo = k.j.i.g.e.c.y.t.e.o.n.ShowPhone,
Cena = k.j.i.g.e.c.y.t.e.o.n.Price,
TitleImage = k.j.i.g.e.c.y.t.e.o.n.TitlePhoto,
})
.OrderByDescending(z => z.Id)
.Take(15);
EDIT: I make what #romfir write bellow and decrase loading time from 1 minute to 3 sec.
Create SQL view
insert SQL query with JOINed tables (if you expect empty value use LEFT JOIN)
Update ADO.NET EF model, .edmx file
use this SQL View simmilar like table

You can try to create View in the database, like this:
CREATE VIEW my_view_name
AS
SELECT
CustomerAdvertisement.ID as CustomerAdvertisementID -- 'as Name' is optional
CustomerAdvertisement.cm3
-- other columns You want to include
FROM
CustomerAdvertisement
JOIN CarManufacturerName
on CarManufacturerName.CarManufacturerId = CustomerAdvertisement.CarManufacturerId
JOIN SomeTable
on some_condition
-- other joins
WHERE
CustomerAdvertisement.Top = true
and other_conditions
After creating view that matches Your criteria, You can scaffold it, and then use in Your code.

Related

Convert sql to linq with STRING_AGG

convert sql to linq that has STRING_AGG
SELECT
V.pkid,
V.[Name] AS VendorName,
SFTP.SFTP_Paths AS SFTP_Paths
FROM
dbo.Vendor V
LEFT OUTER JOIN (
SELECT
connected_to,
STRING_AGG(rootfolder, ', ') AS SFTP_Paths
FROM
dbo.FTP
WHERE
connected_to_type = 4 -- Vendor
GROUP BY
connected_to
) SFTP ON v.pkid = SFTP.connected_to
WHERE
V.active = 1
order by
V.[name]
This query returns the same sql query result.
Instead of STRING_AGG (rootfolder, ',') AS SFTP_Paths I used SFTP_Paths = string.Join(",", b.Select (c => c.rootfolder).ToArray()) for equivalence.
The rest of the query is understandable.
var query = vendorList.Join(lstFtp.Where(x => x.connected_to_type == 4).GroupBy(a => a.connected_to)
.Select(b => new
{
connected_to = b.Key,
SFTP_Paths = b.Select(c => c.rootfolder).ToList()
}).AsEnumerable()
.Select(b => new
{
connected_to = b.connected_to,
SFTP_Paths = string.Join(",", b.SFTP_Paths).ToArray()
}),
right => right.pkid,
left => left.connected_to,
(right, left) => new
{
V = right,
SFTP = left
}).Where(d => d.V.active == 1)
.Select(e => new
{
pkid = e.V.pkid,
VendorName = e.V.Name,
SFTP_Paths = e.SFTP.SFTP_Paths
})
.OrderBy(e => e.VendorName).ToList();

Search column names and tables

Is there any way to search your database for column names or tables in linqpad. Im looking for a similar feature that you can get in SSMS through red gates sql search.
You can get the table and column names from the Linq mapping. The following should dump out the table and column names.
var columns =
(from t in this.Mapping.GetTables()
from dm in t.RowType.DataMembers
where dm.DbType != null
select new
{
TableName = t.RowType.Name ,
TableSqlName = t.TableName,
dm.DbType,
ColumnName = dm.Name,
dm.IsPrimaryKey,
ColumnSqlName = dm.MappedName
}
);
columns.Dump();
So it should be straightforward to filter this query.
If you enable system tables under properties for your connection you can use a query like this (this is for MS SQL but you can probably adapt it to others)
void Main()
{
var text = "ThingToFind";
SearchColumns(text).Dump("Columns: " + text);
SearchModules(text).Dump("Modules: " + text);
}
#region
IEnumerable<dynamic> SearchColumns(string text)
{
return sys
.columns
.Join(sys.objects, o => o.object_id, i => i.object_id, (o, i) => new { Object = i, Column = o })
.Join(sys.types, o => o.Column.user_type_id, i => i.user_type_id, (o, i) => new { o.Column, o.Object, Type = i })
.Where(c => c.Object.type_desc != "INTERNAL_TABLE")
.Where(c => c.Object.type_desc != "SYSTEM_TABLE")
.OrderBy(c => c.Object.type)
.ThenBy(c => c.Object.name)
.Select(c => new { c.Object, c.Column, c.Type, Default = c.Column.default_object_id != 0 ? sys.default_constraints.Single(d => d.object_id == c.Column.default_object_id).definition : null })
.Select(c => new { Table_Type = c.Object.type_desc, Table = c.Object.name, Name = c.Column.name, Type = c.Type.name, Length = c.Column.max_length, Precision = c.Column.precision, Scale = c.Column.scale, Nullable = c.Column.is_nullable, c.Default })
.AsEnumerable()
.Where(c => c.Name.ContainsIgnoreCase(text));
}
IEnumerable<dynamic> SearchModules(string text, bool findRelatedModules = false)
{
var modules = sys
.sql_modules
.AsEnumerable()
.Join(sys.objects, o => o.object_id, i => i.object_id, (o, i) => new { i.name, definition = o.definition.Trim() })
.ToList();
var result = modules
.Where(m => m.name.ContainsIgnoreCase(text) || m.definition.ContainsIgnoreCase(text))
.ToList();
while (findRelatedModules)
{
var add = result
.SelectMany(r => r.definition.Split(" \t\n\r!##$%^&*()-=+[]{};':\",.<>/?\\|`~".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
.Distinct()
.Where(token => modules.Any(m => m.name.ToLower() == token.ToLower()))
.Where(token => !result.Any(m => m.name.ToLower() == token.ToLower()))
.ToList();
result.AddRange(add.Select(a => modules.Single(m => m.name.ToLower() == a.ToLower())));
findRelatedModules = add.Any();
}
result
.Where(m => !m.definition.ContainsIgnoreCase(m.name))
.Dump("Renamed Modules");
return result.OrderBy(r => r.name);
}
#endregion
public static class StringExtensions
{
public static bool ContainsIgnoreCase(this string source, string toCheck, bool bCaseInsensitive )
{
return source.IndexOf(toCheck, bCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) >= 0;
}
}

How can I convert a SQL script into Linq

I do have a sql script that I can't translate into linq. May one of you can help me out to get the right direction.
My big problems are the count and the group by:
SELECT
wfs.ServerId,
COUNT(wfss.Name) AS Records,
wfs.DiskId
FROM WorkflowStep wfs
INNER JOIN WorkflowStepStatus wfss ON wfs.WorkflowStepStatusId = wfss.Id
WHERE wfs.WorkflowId = (
SELECT
Id
FROM Workflow wf
WHERE wf.Name = 'Collecting data virutal'
)
AND wfs.StepNumber IN (1, 2, 3)
AND wfss.Name = 'Processed'
GROUP BY wfs.ServerId,
wfss.Name,
wfs.DiskId
this should work
var result = wfs.Join(wfss,
t => t.WorkflowStepStatusId,
u => u.ID,
(t, u) => new {
t.ServerID,
t.WorkflowId,
t.StepNumber,
u.Name,
t.DiskID
})
.Where(t => t.WorkflowId == wf.FirstOrDefault(u => u.Name == "Collecting data virutal").ID &&
t.Name == "Processed" &&
new List<int> { 1, 2, 3 }.ToArray().Contains(t.StepNumber))
.GroupBy(t => new { t.ServerID, t.Name, t.DiskID })
.Select(t => new {
t.Key.ServerID,
Records = t.Key.Name.Count(),
t.Key.DiskID
})
.ToList();

C# + LINQ - inner join

Table: one
Column (PK): employee_ssn
Column: employee_active bit
Table: two
Column (pk): date
Column (FK): employee_ssn
Column: total_income
Column: total_expenses
Working Linq code to sum total_income and total_expenses for the year, per employee:
var SumOfSections = db.two
.Select(x => x)
.Where(x => x.employee_ssn.Equals(xxxxxxxxx))
.Where(x => x.Date.Year.Equals(year))
.GroupBy(x => x.Date)
.Select(g => new
{
Total_Income = g.Sum(x => x.total_expenses),
Total_Expenses= g.Sum(x => x.total_income)
})
.ToArray();
I need to incorporate INNER JOIN to the above to only include active employees. Working SQL:
select two.total_income,total_expenses
from two
INNER JOIN one
ON one.SSN = two.SSN
WHERE one.Active = 1 AND two.Date='1/1/2014' AND two.SSN='xxxxxxxxx';
How can I modify my linq code to what my sql code is doing?
var SumOfSections = (from t in db.two
join o in db.one on t.employee_ssn equald o.employee_ssn
where t.employee_ssn = "xxxxxxxxx" && o.employee_active == true
group t by t.date into g
select new {
Total_Income = g.Sum(x => x.total_expenses),
Total_Expenses= g.Sum(x => x.total_income)
}).ToArray();
I used query syntax because it seems to be more readable.
You can also continue with the notation you are using:
var SumOfSections = db.two.Join(db.one, o=>o.employee_ssn, t=>t.employee_ssn, (o,t)=>new {One = o, Two = t)
.Select(x => x)
.Where(x => x.Two.employee_ssn.Equals(""))
.Where(x => x.Two.date.Year.Equals(1234))
.Where(x=> x.One.employee_active == true)
.GroupBy(x => x.Two.date)
.Select(g => new
{
Total_Income = g.Sum(x => x.Two.total_expenses),
Total_Expenses = g.Sum(x => x.Two.total_income)
})
.ToArray();

How to use the group join result to join another tables using LINQ?

I am not able to use the result of group join and then further applying the join with other tables.
My SQL query is :
SELECT *
FROM
[dummy_database].[dbo].[MA] M
INNER JOIN
SN.dbo.A A ON A.ApplicantMAId = M.MAId
INNER JOIN
SN.dbo.SP SP ON SP.PropertyMAId = M.MAId
INNER JOIN
SN.dbo.MCC CC ON CC.MAId = m.MAId
INNER JOIN
SN.dbo.MLSTN T ON T.MAT_ID = M.MAId
INNER JOIN
(SELECT
MAX(MAT_DATE) AS MaxDate,
MAT_ID
FROM
[SN].[dbo].[MLSTN]
GROUP BY
MAT_ID) Q ON Q.MAT_ID = M.MAId
and what I have done so far is:
var q = (from ml in context.MLSTN
group ml by ml.MAT_ID into g
let maxdate = g.Max(date => date.MAT_DATE)
select new
{
MortgId = g.Key,
TrackingDate = g.FirstOrDefault(val => val.MAT_DATE == maxdate).MAT_DATE
}
);
but now this result is not at all further used by me to create a join with other tables. I want to ask how to join further? Any clue?
You need something like this:
var groups = context.MLSTN
.GroupBy(x => x.MAT_ID)
.Select(g => new
{
MAT_ID = g.Key,
MaxDate = g.Max(date => date.MT_DATE)
});
var result = context.MA
.Join(context.A,
m => m.MA_ID,
a => a.MA_ID,
(m, a) => new
{
MA = m,
A = a
})
.Join(context.SP,
x => x.MA.MAId,
sp => sp.PropertyMAId,
(x, sp) => new
{
MA = x.MA ,
A = x.A,
SP = sp
})
.Join(context.MCC,
x => x.MA.MAId,
cc => cc.MAId,
(x, cc) => new
{
MA = x.MA ,
A = x.A,
SP = x.SP,
MCC = cc
})
.Join(context.MLSTN,
x => x.MA.MAId,
t => t.MAT_ID,
(x, t) => new
{
MA = x.MA ,
A = x.A,
SP = x.SP,
MCC = x.MCC,
MLSTN = t
})
.Join(groups,
x => x.MA.MAId,
g => g.MAId,
(x, g) => new
{
MA = x.MA ,
A = x.A,
SP = x.SP,
MCC = x.MCC,
MLSTN = t,
MLSG = g
});