How can I do this in a single select statement?
var data = new CampaignData()
{
TotalPost = await context.Campaigns.SumAsync(c => c.Posts),
AveragePost = await context.Campaigns.AverageAsync(c => c.Posts),
TotalImpression = await context.Campaigns.SumAsync(c => c.Impressions),
AverageImpressions = await context.Campaigns.AverageAsync(c => c.Impressions),
};
You can group by a constant so you can get the sums and averages. Then use SingleAsync to get the single result.
var data = await (from compaign in context.Compaigns
group compaign by 1 into grp
select new CampaignData()
{
TotalPost = grp.Sum(cc => cc.Posts),
AveragePost = grp.Average(c => c.Posts),
TotalImpression = grp.Sum(c => c.Impressions),
AverageImpressions = grp.Average(c => c.Impressions),
}).SingleAsync();
Another option is to actually let your asynchronous DB calls run in parallel
var totalPostTask = context.Campaigns.SumAsync(c => c.Posts);
var averagePostTask = context.Campaigns.AverageAsync(c => c.Posts);
var totalImpressionTask = context.Campaigns.SumAsync(c => c.Impressions);
var averageImpressionsTask = context.Campaigns.AverageAsync(c => c.Impressions);
await Task.WhenAll(
totalPostTask,
averagePostTask,
totalImpressionTask,
averageImpressionsTask);
var data = new CampaignData()
{
TotalPost = totalPostTask.Result,
AveragePost = averagePostTask.Result,
TotalImpression = totalImpressionTask.Result,
AverageImpressions = averageImpressionsTask.Result,
};
If I had to guess I'd say the single DB call would perform better, but you can always test both options out to see which is better.
Related
Coming from SQL background I am used to joining 5-8 tables in single query.
I can not imagine doing the same with Linq lambda expression syntax.
Here I am joining 4-5 tables/collections.
EG:
var viewmodel = logs.Join(CargoElements.lstContainerLoadStatus.ToList(), l => l.LoadStatus, ls => ls.Value, (x, ls) =>
new
{
log = x,
LoadStatusDesc = ls.Text
})
.Join(db.CargoContainerSize, log => log.log.CargoContainerSizeID, ccsize => ccsize.Id, (log, ccsize) => new
{
log = log,
CargoContainerSizeIDDesc = ccsize.Size
}).Join(db.CargoContainerType, log => log.log.log.CargoContainerTypeID, cct => cct.Id, (log, cct) => new
{
log = log,
CargoContainerTypeIDDesc = cct.Name
}).GroupJoin(db.CargoFrom, log => log.log.log.log.CargoFromID, cf => cf.ID, (log, cf) => new
{
log = log,
cf = cf
}).SelectMany(temp => temp.cf.DefaultIfEmpty(), (temp, cf) => new
{
log = temp,
CargoFromIDDesc = cf.Description
}
)
.Select(x => new ContainerInLogsVM
{
ContainerInLogID = x.log.log.log.log.log.ContainerInLogID,
ContainerInID = x.log.log.log.log.log.ContainerInID,
CargoID = x.log.log.log.log.log.CargoID,
LoadStatus = x.log.log.log.log.log.LoadStatus,
LoadStatusDesc = x.log.log.log.log.LoadStatusDesc,
Shipper = x.log.log.log.log.log.Shipper,
CnFAgentName = x.log.log.log.log.log.CnFAgentName,
ShippingBill = x.log.log.log.log.log.ShippingBill,
ContainerNo = x.log.log.log.log.log.ContainerNo,
CargoContainerSizeID = x.log.log.log.log.log.CargoContainerSizeID,
CargoContainerSizeIDDesc = x.log.log.log.CargoContainerSizeIDDesc,
CargoContainerTypeID = x.log.log.log.log.log.CargoContainerTypeID,
CargoContainerTypeIDDesc = x.log.log.CargoContainerTypeIDDesc,
OtherType = x.log.log.log.log.log.OtherType,
VesselNo = x.log.log.log.log.log.VesselNo,
ShipperBillNo = x.log.log.log.log.log.ShipperBillNo,
SealOTLNo = x.log.log.log.log.log.SealOTLNo,
VoyageNo = x.log.log.log.log.log.VoyageNo,
BLNumber = x.log.log.log.log.log.BLNumber,
Purpose = x.log.log.log.log.log.Purpose,
CargoFromID = x.log.log.log.log.log.CargoFromID,
CargoFromIDDesc = x.CargoFromIDDesc,
OtherFrom = x.log.log.log.log.log.OtherFrom,
Status = x.log.log.log.log.log.Status,
Remark = x.log.log.log.log.log.Remark,
StatusChangedOn = x.log.log.log.log.log.StatusChangedOn,
StatusChangedBy = x.log.log.log.log.log.StatusChangedBy,
StatusChangedByDesc = "",
SysRemark = x.log.log.log.log.log.SysRemark
}
).ToList();
This is ridiculous! Or am I doing it wrong? There has to be a better way in LINQ.
I am looking for answers preferably in lambda expressions.
I'm curious to know what the difference is between the two statements below and why the .ForEacHAsync doesn't work for creating new rows but the for loop does?
this works and adds new record products
var recordProducts = context.RecordsProducts
.Where(i => i.RecordId == model.OldRecordId);
foreach (var rp in recordProducts)
{
var newRecordProduct = new RecordProduct
{
IsActive = true,
RecordId = model.RecordId,
ProductId = rp.ProductId,
DefendantId = rp.DefendantId,
Annotation = rp.Annotation
};
context.RecordsProducts.Add(newRecordProduct);
}
this doesn't
var recordProducts = context.RecordsProducts
.Where(i => i.RecordId == model.OldRecordId)
.ForEachAsync(a =>
{
var newRecordProduct = new RecordProduct
{
IsActive = true,
RecordId = model.RecordId,
ProductId = a.ProductId,
DefendantId = a.DefendantId,
Annotation = a.Annotation
};
context.RecordsProducts.Add(newRecordProduct);
}
);
In the first example, your IQueryable<RecordProduct> recordProducts will be evaluated synchronously, not asynchronously, so it will block the thread inside the (hidden) call to IQueryable.GetEnumerator() ...MoveNext().
Whereas in the second example, the .ForEachAsync extension method will run the anonymous function asynchronously and is equivalent to this:
IQueryable<RecordProduct> list = await context.RecordsProducts
.Where(i => i.RecordId == model.OldRecordId);
using( DataReader rdr = await ExecuteQueryAsDataReader( list ) )
{
while( await rdr.ReadAsync() )
{
await ForEachAsyncBodyHere();
}
}
Your second example doesn't work because the the result of your expression is a Task which is never awaited. If you want to use ForEachAsync you need to change your code to this:
Task loadTask = context.RecordsProducts
.Where(i => i.RecordId == model.OldRecordId)
.ForEachAsync(a =>
{
var newRecordProduct = new RecordProduct
{
IsActive = true,
RecordId = model.RecordId,
ProductId = a.ProductId,
DefendantId = a.DefendantId,
Annotation = a.Annotation
};
context.RecordsProducts.Add(newRecordProduct);
}
);
await loadTask; // This will wait (actually, _yield_) until all of the `ForEachAsync` iterations are complete.
await context.SaveChangesAsync(); // This will actually save the new rows added to `context.RecordsProducts`
I don't think either piece of code is necessarily good - I think the best approach would be to load all the data asynchronously at-once using ToListAsync and then use a normal synchronous foreach to Add each record then await SaveChangesAsync:
List<RecordProduct> list = await context.RecordsProducts
.Where(i => i.RecordId == model.OldRecordId)
.ToListAsync();
foreach( RecordProduct rp in list )
{
context.RecordsProduct.Add( ... );
}
await context.SaveChangesAsync();
I am trying to fill select tag options from JQuery ajax call. I am using Asp.Net Core 2.1 Razor Pages, and PostgreSQL as DB.
Here is my Server side LINQ code
[HttpGet]
public ActionResult TypeofAccounts()
{
var result = (from N in _POSContext.TypeOfAccounts
select new { label = N.AccountType, id = N.AccountType });
return Json(result);
}
It works fine. Now, I want to sort those results from LINQ so I tried following ways but it always encounters Npgsql Exception "column \"label\" does not exist"
var result = (from N in _POSContext.TypeOfAccounts.OrderBy(x=>x.AccountType)
select new { label = N.AccountType, id = N.AccountType });
var result = (from N in _POSContext.TypeOfAccounts
select new { label = N.AccountType, id = N.AccountType }).OrderBy(x => x.label);
var result = (from N in _POSContext.TypeOfAccounts.OrderBy(x => x.AccountType)
where N.AccountType != null
select new { label = N.AccountType, id = N.AccountType });
I could see coloumn is missing in generated sql.
{SELECT x."AccountType" AS id
FROM "TypeOfAccounts" AS x
WHERE x."AccountType" IS NOT NULL
ORDER BY label}
You need to invoke the query from the database using ToList method, then selecting your object, like this:
var result = _POSContext.TypeOfAccounts
.Where(x => x.AccountType != null)
.OrderBy(x => x.AccountType)
.ToList()
.Select(x =>
new
{
label = x.AccountType,
id = x.AccountType
}
);
You can try this
var result = _POSContext.TypeOfAccounts
.Where(x => x.AccountType != null)
.OrderBy(x => x.AccountType)
.ToList()
.Select(x =>
new
{
label = x.AccountType,
id = x.AccountType
}
);
I have a query that is currently far too slow.
I am trying to search a Code (a string) on the main page that will bring the user the relevant info.
Eg. The user can search a code from the main page and this will search for the code in Job, Work Phase, Wbs, Work Element, EA, Jobcard and Estimate and return the relevant info.
I make a number of trips to the database to collect the data i need when I believe it can be done in just one.
I have a number of tables that are all linked:
Contracts, Jobs, WorkPhases, Wbss, Engineering Activities, Jobcards and Estimates.
Contracts have a list of Jobs,
Jobs have a list of Workphases,
Workphases have a list of Wbss etc
Is there a quicker way to do this?
public Result Handle(Query query)
{
query.Code = query.Code ?? string.Empty;
var result = new Result();
//result.SetParametersFromPagedQuery(query);
result.Items = new List<Item>();
if (query.SearchPerformed)
{
var contracts = _db.Contracts.AsEnumerable().Where(x => x.Code == query.Code);
result.Items = result.Items.Concat(contracts.Select(x => new Item()
{
Code = x.Code,
Id = x.Id,
Name = x.Name,
Type = MainPageSearchEnum.Contract,
ContractName = x.Name,
Url = string.Format("Admin/Contract/Edit/{0}", x.Id)
})).ToList();
var jobs = _db.Jobs.AsEnumerable().Where(x => x.Code == query.Code);
result.Items = result.Items.Concat(jobs.Select(x => new Item()
{
Code = x.Code,
Id = x.Id,
Name = x.Name,
ContractName = x.Contract.Name,
Type = MainPageSearchEnum.Job,
Url = string.Format("Admin/Job/Edit/{0}", x.Id)
})).ToList();
//var workPhases = _db.WorkPhases.AsEnumerable().Where(x => x.ContractPhase.Code.ToLower() == query.Code.ToLower());
var workPhases = _db.WorkPhases.AsEnumerable().Where(x => x.ContractPhase.Code == query.Code);
result.Items = result.Items.Concat(workPhases.Select(x => new Item()
{
Code = x.ContractPhase.Code,
Id = x.Id,
Name = x.ContractPhase.Name,
Type = MainPageSearchEnum.WorkPhase,
Url = string.Format("Admin/WorkPhase/Edit/{0}", x.Id)
})).ToList();
var wbss = _db.WBSs.AsEnumerable().Where(x => x.Code == query.Code);
result.Items = result.Items.Concat(wbss.Select(x => new Item()
{
Code = x.Code,
Id = x.Id,
Name = x.Name,
Type = MainPageSearchEnum.WBS,
Url = string.Format("Admin/WBS/Edit/{0}", x.Id)
})).ToList();
var eas = _db.EngineeringActivities.AsEnumerable().Where(x => x.Code == query.Code);
result.Items = result.Items.Concat(eas.Select(x => new Item()
{
Code = x.Code,
Id = x.Id,
Name = x.Name,
Type = MainPageSearchEnum.EA,
Url = string.Format("Admin/EngineeringActivity/Edit/{0}", x.Id)
})).ToList();
var jcs = _db.Jobcards.AsEnumerable().Where(x => x.Code == query.Code);
result.Items = result.Items.Concat(jcs.Select(x => new Item()
{
Code = x.Code,
Id = x.Id,
Name = x.Name,
Type = MainPageSearchEnum.EA,
Url = string.Format("Admin/JobCard/Edit/{0}", x.Id)
})).ToList();
var estimates = _db.Estimates.AsEnumerable().Where(x => x.Code == query.Code);
result.Items = result.Items.Concat(estimates.Select(x => new Item()
{
Code = x.Code,
Id = x.Id,
Name = x.Name,
Type = MainPageSearchEnum.Estimate,
Url = string.Format("Estimation/Estimate/Edit/{0}", x.Id)
})).ToList();
}
return result;
}
Disclaimer: I'm the owner of the project Entity Framework Plus
This library has a Query Future feature which allows batching multiple queries in a single roundtrip.
Example:
// using Z.EntityFramework.Plus; // Don't forget to include this.
var ctx = new EntitiesContext();
// CREATE a pending list of future queries
var futureCountries = ctx.Countries.Where(x => x.IsActive).Future();
var futureStates = ctx.States.Where(x => x.IsActive).Future();
// TRIGGER all pending queries in one database round trip
// SELECT * FROM Country WHERE IsActive = true;
// SELECT * FROM State WHERE IsActive = true
var countries = futureCountries.ToList();
// futureStates is already resolved and contains the result
var states = futureStates.ToList();
Wiki: EF+ Query Future
Have you tried the Union / UnionAll operator?
It's purpose is exactly like you wish - combine the identical data from different sources.
Furthermore due to the concept of deferred execution your query will only be executed when you actually iterate over the result (or call a method that does that, for example - .ToList()
var contractsQuery = _db.Contracts.AsEnumerable().Where(x => x.Code == query.Code).Select(x=>new {Code=x.Code, Id=x.Id, ...});
var jobsQuery = _db.Jobs.AsEnumerable().Where(x => x.Code == query.Code).Select(x=>new{Code=x.Code, Id=x.Id, ...});
var workPhasesQuery = _db.WorkPhases.AsEnumerable().Where(x => x.ContractPhase.Code == query.Code).Select(x=>new{Code=x.Code, Id=x.Id, ...});
// and so on
var combinedQuery = contractsQuery.UnionAll(jobsQuery).UnionAll(workPhasesQuery ).UnionAll(...
var result = combinedQuery.ToList();
A similar question is Union in linq entity framework
Another code sample can be found here
Please notice that this is exactly the same concept of manipulating data as in T-SQL union, and under the covers you will get an sql query using a union operator
Yes there most certainly is a way to query multiple tables. You can use the Include() method extension for your query. for instance:
var examplelist = _db.Contracts.(v => v.id == "someid" && v.name == "anotherfilter").Include("theOtherTablesName").ToList();
You can include as many tables as you like this way. This is the recommended method.
You can also use the UnionAll() method but you'd have to define your queries separately for this
Ok, so I have a need to create/return a Dictionary from the results of a Linq Query. I have tried just about everything I can think of and keep running into issues. Here is what I am currently attempting...
public static Dictionary<int,int[]> GetEntityAuthorizations(int userId)
{
using (MyDb db = new MyDb())
{
var query = db.EntityManagerRoleAssignments.Where(x => x.EntityManager.ManagerId == userId);
var entityId = query.Select(x => x.EntityManager.EntityId);
var roles = query.Select(x => x.RoleId).ToArray();
var result = query.ToDictionary(entityId, roles);
return result;
}
}
any help at all would be greatly appreciated. what i am looking for to be returned from this is a Dictionary where the Key is the entityId or EntityManager.EntityId and the Value(s) are an array of associated RoleId's.
Currently I am getting the following two errors at compile time, and other attempts have been errors similar but not exact to these.
Error 11 The type arguments for method 'System.Linq.Enumerable.ToDictionary<TSource,TKey>(System.Collections.Generic.IEnumerable, System.Func<TSource,TKey>, System.Collections.Generic.IEqualityComparer<TKey>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Error 12 Cannot implicitly convert type 'System.Collections.Generic.Dictionary<TKey,Sqor.Database.DbEntityManagerRoleAssignment>' to 'System.Collections.Generic.Dictionary<int,int[]>'
UPDATE - Working Solution (Thanks to #D Stanley)
public static Dictionary<int,int[]> GetEntityAuthorizations(int userId)
{
using (SqorDb db = new SqorDb())
{
var query = db.EntityManagerRoleAssignments.Where(x => x.EntityManager.ManagerId == userId);
var entityGroups = query.GroupBy(x => x.EntityManager.EntityId);
var result = entityGroups.ToDictionary(e => e.Key,
g => g.Select(x => x.RoleId).ToArray()
);
return result;
}
}
It sounds like you want to group by the Entity ID and project the associated role IDs to an array:
using (MyDb db = new MyDb())
{
var query = db.EntityManagerRoleAssignments.Where(x => x.EntityManager.ManagerId == userId);
var entityGroups = query.GroupBy(x => x.EntityManager.EntityId);
var result = entityGroups.ToDictionary(e => e.Key,
g => g.Select(x => x.RoleId).ToArray()
);
return result;
}