How can I do an outer join with EF 6.1 - c#

I have the following:
var tests = await db.Tests
.Include(t => t.Exam)
.Where(t => t.TestStatusId == 1)
.Select(t => new TestDTO
{
ExamName = t.Exam.Name,
Id = t.TestId,
QuestionsCount = t.QuestionsCount,
Title = t.Title
})
.ToListAsync();
return Ok(tests);
How can I make this so that it still returns tests even if there is no matching Exam for a particular test?

Try this:
//first step
var tests = db.Tests
.Include(t => t.Exam)
.Where(t => t.TestStatusId == 1);
//next step
if(testc.Exam != null && testc.Exam.Count > 0)
{
testc = testc.Select(t => new TestDTO
{
ExamName = t.Exam.Name,
Id = t.TestId,
QuestionsCount = t.QuestionsCount,
Title = t.Title
});
}

Related

Duplicated linq query

I have 2 almost identical linq queries and want to remove repeating code from it. The only difference is the extra property in the GroupBy depending on some true/false condition.
How can I conditionally group by in linq without repeating the code like below?
var allergensList = _context.RecipeAllergens
.Where(x => x.ParentId == Id && x.AllergenId != null)
.ToList();
var allergens = new List<AllergenInfo>();
if (isRecipe)
{
allergens = allergensList
.GroupBy(x => new { x.AllergenName, x.AllergenIcon, x.AllergenMaycontains })
.Select(a =>
{
var v = a.OrderBy(x => x.AllergenMaycontains).First();
return new AllergenInfo
{
AllergenName = v.AllergenName,
AllergenIcon = v.AllergenIcon,
AllergenMayContain = v.AllergenMaycontains ?? false
};
})
.ToList();
}
else
{
allergens = allergensList
.GroupBy(x => new { x.AllergenName, x.AllergenIcon })
.Select(a =>
{
var v = a.OrderBy(x => x.AllergenMaycontains).First();
return new AllergenInfo
{
AllergenName = v.AllergenName,
AllergenIcon = v.AllergenIcon,
AllergenMayContain = v.AllergenMaycontains ?? false
};
})
.ToList();
}
You can left grouping by x.AllergenMaycontains but under condition
allergens = allergensList
.GroupBy(x => new { x.AllergenName, x.AllergenIcon, AllergenMaycontains = isRecipe ? x.AllergenMaycontains : false })
.Select(a =>
{
var v = a.OrderBy(x => x.AllergenMaycontains).First();
return new AllergenInfo
{
AllergenName = v.AllergenName,
AllergenIcon = v.AllergenIcon,
AllergenMayContain = v.AllergenMaycontains ?? false
};
})
.ToList();

Understanding EF core subquery [duplicate]

This question already has answers here:
Multiple aggregate functions in one query using Linq to SQL [duplicate]
(1 answer)
Return multiple aggregate columns in LINQ
(4 answers)
Closed last year.
I have the following linq statements which I am hoping to cut down from multiple round trips to my database to one using subquery.
This is my code before refactoring:
IQueryable<Customers> customers= this.BaseQuery.Where(o => o.EmailAddress == emailAddress);
int totalCustomersCount = await customers.CountAsync();
int totalDisabledCustomersCount = await customers.Where(o => CustomerState.Disabled == o.CurrentState).CountAsync();
int totalCancelledCustomersCount = await customers.Where(o => CustomerState.Cancelled == o.CurrentState).CountAsync();
int totalExpiredCustomersCount = await customers.Where(o => CustomerState.Archived == o.CurrentState).CountAsync();
int totalPremiumCustomerCount = await customers.Where(o => CustomerState.Premium== o.CurrentState).CountAsync();
CustomerStatistics customerStatistics = new CustomerStatistics()
{
TotalCustomersCount = totalCustomersCount,
TotalDisabledCustomersCount = totalDisabledCustomersCount,
TotalCancelledCustomersCount = totalCancelledCustomersCount,
TotalExpiredCustomersCount= totalExpiredCustomersCount,
TotalPremiumCustomerCount= totalPremiumCustomerCount,
};
and I have reworked this code to look like this
IQueryable<Customers>? query = this.Context.Customers.Where(o => o.EmailAddress == emailAddress);
IQueryable<CustomerStatistics >? myQuery= query.Select(o => new CustomerStatistics()
{
TotalCustomersCount = query.Count(),
TotalDisabledCustomersCount = query.Where(o => o.CurrentState == CustomerState.Disabled).Count(),
TotalCancelledCustomersCount = query.Where(o => o.CurrentState == CustomerState.Cancelled).Count(),
TotalExpiredCustomersCount= query.Where(o => o.CurrentState == CustomerState.Archived).Count(),
TotalPremiumCustomerCount= query.Where(o => o.CurrentState == CustomerState.Premium).Count(),
});
Is this along the right lines? To me, it still looks like it will be makign round trips to the database to get each count.
Update to the example query:
IQueryable<Order> orders = this.BaseQuery.Where(o => o.CustomerContactId == customerContactId);
int totalOrderCount = await orders.CountAsync();
int totalInProgressOrderCount = await orders.Where(o => OrderState.Fulfilment == o.CurrentState).CountAsync();
int totalCancelledOrderCount = await orders.Where(o => OrderState.Cancelled == o.CurrentState).CountAsync();
int totalExpiredOrderCount = await orders.Where(o => OrderState.Archived == o.CurrentState).CountAsync();
int totalQuoteCount = await orders.Where(o => OrderState.Initial == o.CurrentState || OrderState.QuoteIssued == o.CurrentState || OrderState.ReadyToPlace == o.CurrentState || OrderState.CheckGeometry == o.CurrentState).CountAsync();
int totalIssuedOrderCount = await orders.Where(o => OrderState.QuoteIssued == o.CurrentState).CountAsync();
CustomerContactOrderStatistics customerContactOrderStatistics = new CustomerContactOrderStatistics()
{
TotalOrderCount = totalOrderCount,
TotalInProgressOrderCount = totalInProgressOrderCount,
TotalCancelledOrderCount = totalCancelledOrderCount,
TotalExpiredOrderCount = totalExpiredOrderCount,
TotalQuoteCount = totalQuoteCount,
TotalIssuedOrderCount = totalIssuedOrderCount,
};
You can do grouping by constant. It is special case which is handled by EF to make aggregation query:
vra query = this.Context.Customers
.Where(o => o.EmailAddress == emailAddress);
var statistics = = query.GroupBy(x => 1)
.Select(g => new CustomerStatistics()
{
TotalCustomersCount = g.Count(),
TotalDisabledCustomersCount = g.Count(o => o.CurrentState == CustomerState.Disabled),
TotalCancelledCustomersCount = g.Count(o => o.CurrentState == CustomerState.Cancelled),
TotalExpiredCustomersCount = g.Count(o => o.CurrentState == CustomerState.Archived),
TotalPremiumCustomerCount = g.Count(o => o.CurrentState == CustomerState.Premium),
});
For EF Core versions which do not support conditional Count, it can be emulated by Sum
var statistics = = query.GroupBy(x => 1)
.Select(g => new CustomerStatistics()
{
TotalCustomersCount = g.Count(),
TotalDisabledCustomersCount = g.Sum(o => o.CurrentState == CustomerState.Disabled ? 1 : 0),
TotalCancelledCustomersCount = g.Sum(o => o.CurrentState == CustomerState.Cancelled ? 1 : 0),
TotalExpiredCustomersCount= g.Sum(o => o.CurrentState == CustomerState.Archived ? 1 : 0),
TotalPremiumCustomerCount= g.Sum(o => o.CurrentState == CustomerState.Premium ? 1 : 0),
});

Linq-to-SQL query (AsEnumerable) on multiple tables

Here's a Linq-to-SQL query that uses only one table from my SQL Server database and works perfectly:
private void GetData()
{
DateTime d = DateTime.Now;
using (DataClasses1DataContext dc = new DataClasses1DataContext())
{
var qte = dc.ENTREES_STOCKS.AsEnumerable()
.Where(x => x.ENTSTK_LOT == lot)
.Where(x => x.ART_CODE == artCode)
.Where(x => x.ENTSTK_USER == null)
.Select(s => new
{
art = s.ART_CODE,
date = s.ENTSTK_DTENTREE,
numLot = s.ENTSTK_LOT,
pnet = s.ENTSTK_PNET,
nbu = s.ENTSTK_NBU
})
.GroupBy(g => new { g.art, g.date, g.numLot })
.Select(n => new
{
n.Key.art,
n.Key.date,
n.Key.numLot,
pnet = n.Sum(x => Math.Round(Convert.ToDecimal(x.pnet), 2)),
nbu = n.Sum(x => Math.Round(Convert.ToDecimal(x.nbu), 2)),
});
QEntreeTB.Text = qte.First().pnet.ToString();
NbuEntreeTB.Text = qte.First().nbu.ToString();
}
}
How could I modify this code to join other tables to this query like :
private void GetData()
{
DateTime d = DateTime.Now;
using (DataClasses1DataContext dc = new DataClasses1DataContext())
{
var qte = dc.ENTREES_STOCKS.AsEnumerable()
// Thoseline of codes of course doesn't work
join art in dc.FICHES_ARTICLES on ENTREES_STOCKS.ART_CODE equals art.ART_CODE
join ent in dc.STK_ENT on art.ART_CODE equals ent.ART_CODE
....
//
.Where(x => x.ENTSTK_LOT == lot)
.Where(x => x.ART_CODE == artCode)
.Where(x => x.ENTSTK_USER == null)
.Select(s =>
new
{
art = s.ART_CODE,
date = s.ENTSTK_DTENTREE,
numLot = s.ENTSTK_LOT,
pnet = s.ENTSTK_PNET,
nbu = s.ENTSTK_NBU
}
)
.GroupBy(g => new { g.art, g.date, g.numLot })
.Select(n =>
new
{
n.Key.art,
n.Key.date,
n.Key.numLot,
pnet = n.Sum(x => Math.Round(Convert.ToDecimal(x.pnet), 2)),
nbu = n.Sum(x => Math.Round(Convert.ToDecimal(x.nbu), 2)),
}
);
QEntreeTB.Text = qte.First().pnet.ToString();
NbuEntreeTB.Text = qte.First().nbu.ToString();
}
}
Or is there à way to code this query another way ??
Because in fact i just want to join multiples tables, groupby some fields and sum others fields.
Firstly, calling AsEnumerable is a bit redundent. Then you can simply use the Join extension method.
var qte = dc.ENTREES_STOCKS
.JOIN(dc.FICHES_ARTICLES,art=>art.ART_CODE, stock => stock.ART_CODE)
.JOIN(dc.STK_ENT,ent => ent.ART_CODE,stock => stock.ART_CODE)
.Where(x => x.ENTSTK_LOT == lot)
.Where(x => x.ART_CODE == artCode)
.Where(x => x.ENTSTK_USER == null)
....
You can find more answers here:
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/method-based-query-syntax-examples-join-operators

Linq to find lasted record in group

I want add a new column to find which is lasted record in group.
Can I write subquery in Select() method?
I have try this
var test = DailyPeriods.Where(x => x.BookingDate == "2016/12/30")
.Select(x =>
new
{
PERIOD_GROUP_ID = x.PeriodGroupID,
PERIOD_NAME = x.PeriodName,
New_Column = DailyPeriods
.Where(z => z.BookingDate == "2016/12/30")
.Select(a =>
new
{
PeriodGroupID = a.PeriodGroupID,
period_name = a.PeriodName
}
)
.GroupBy(b => b.period_name)
.Select(g => g.Last().PeriodGroupID)
.Contains(x.PeriodName)
})
But will occur this error
"column not in scope: A:2211708.C(BOOKING_DATE)"
Try this..
var lastRecords = periodList.GroupBy(l => l.PeriodName)
.Select(x => new { PeriodName = x.Key,
PeriodGroupId = x.OrderBy(l => l.PeriodGroupId).Last().PeriodGroupId});
var result = from period in periodList
from lastRec in lastRecords.Where(r => r.PeriodGroupId == period.PeriodGroupId
&& r.PeriodName == period.PeriodName)
.DefaultIfEmpty()
select new { period.PeriodGroupId,period.PeriodName, New_Column=lastRec==null?false:true };

How to use LINQ with calculated properties in a single pass?

I'd like to make a LINQ query, extracting dynamic properties (calculated fields) of my entities in a single pass, without get the error "The specified type member 'EntityKey' is not supported in LINQ to Entities".
Here is the only working way I found, but I am sure there are better and more elegant methods:
var q = (from i in
(from x in context.Tickets
select new { x.OperatoreID, x.DataObiettivo })
group i by new { i.OperatoreID } into g
select new vmOperatoreDateObiettivo
{
OperatoreID = g.Key.OperatoreID,
NOperatore = "", // field value to be updated...
DataObiettivo = g.Max(d => d.DataObiettivo),
MinutiAllaScadenza = 0, // field to be updated...
Alert = "" // field value to be updated...
}).ToList();
// Here I update my fields with a second pass....
foreach (vmOperatoreDateObiettivo e in q)
{
string nome = context.Operatori
.Where(t => t.OperatoreID == e.OperatoreID)
.First().CognomeNomePuntato.ToString();
e.NOperatore = nome;
int minscad = context.Tickets
.Where(t => t.OperatoreID == e.OperatoreID).AsEnumerable().Min(a => a.MinutiAllaScadenza);
e.MinutiAllaScadenza = minscad;
string sev = context.Tickets
.Where(t => t.OperatoreID == e.OperatoreID).AsEnumerable().Min(a => a.Alert);
e.Alert = sev;
}
Thanks in advance!
Try adding a let clause to your query and define calculated field, like so:
var q = (from i in
(from x in context.Tickets
select new { x.OperatoreID, x.DataObiettivo })
group i by new { i.OperatoreID } into g
let nOperatore = context.Operatori
.Where(t => t.OperatoreID == e.OperatoreID)
.First().CognomeNomePuntato.ToString() &&
minutialla = context.Tickets
.Where(t => t.OperatoreID == e.OperatoreID)
.AsEnumerable().Min(a => a.MinutiAllaScadenza) &&
alert = context.Tickets
.Where(t => t.OperatoreID == e.OperatoreID)
.AsEnumerable().Min(a => a.Alert)
select new vmOperatoreDateObiettivo
{
OperatoreID = g.Key.OperatoreID,
NOperatore = nOperatore,
DataObiettivo = g.Max(d => d.DataObiettivo),
MinutiAllaScadenza = minutialla,
Alert = alert
}).ToList();

Categories

Resources