I have two related entities with composite keys:
public class Event
{
public string ID1 { get; set; }
public int ID2 { get; set; }
public DateTime EventDate { get; set; }
public string EventData { get; set; }
public string DocID1 { get; set; }
public int DocID2 { get; set; }
}
public class EventDocument
{
public string ID1 { get; set; }
public int ID2 { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Number { get; set; }
public virtual ICollection<Event> Events { get; set; }
}
Is there a possibility to filter first both of them by some criteria and then to join results because of big amount of records?
Acctually I can reach related Events when I filter EventDocuments, but I also need a possibility to filter Event and EventDocument in one time.
I am trying to do like this:
var events = ModelContext.Events.AsNoTracking().Select(x => x);
events = events.Where(x => x.EventData.StartsWith(FilterCriteria));
var eventDocuments = ModelContext.EventDocuments.AsNoTracking().Select(x => x);
eventsDocuments = eventDocuments.Where(x => x.LastName.StartsWith(FilterLastName));
And now I need to join these to queries and get a result - filtered and joined data from two entities
Trying to do like this:
var result = eventDocuments.Join(events,
doc => new { doc.ID1, doc.ID2 },
ev => new { cross.DocID1, cross.DocID2},
(doc, ev) => new { EventDocument = doc, Event = ev });
You can simply query both sets with SelectMany. In query syntax this would look like:
var eventsQry =
from eventDocument in eventDocuments
where eventDocument.LastName.StartsWith(FilterLastName)
from ev in events
where ev.EventData.StartsWith(FilterCriteria) && (ev.ID1 == eventDocument.ID1) && (ev.ID2 == eventDocument.ID2)
select new { eventDocument, ev };
You don't need to use one query to filter your results. You can combine multiple queries:
var eventsQry =
from ev in events
where ev.EventData.StartsWith(FilterCriteria)
select ev
var documentsQry =
from eventDocument in documentsQry
where eventDocument.LastName.StartsWith(FilterLastName)
select eventDocument;
var combinedQry =
from eventDocument in documentsQry
from ev in eventsQry
where (ev.ID1 == eventDocument.ID1) && (ev.ID2 == eventDocument.ID2)
select new { eventDocument, ev };
Related
Good day,
I am doing Web api rest project and want to include product search for products by size and color, but I want to be able search for example:
1 One size
[httpGet][Route("oneSize/{sizeID}")]
2 Two Sizes
[httpGet][Route("TwoSizes/{sizeID1}/{sizeID2}")]
3 One size/ One color
[httpGet][Route("OneSizeOneColor/{sizeID1}/{ColorID}")]
4 Two sizes/ One color
[httpGet][Route("TwoSizeOneColor/{sizeID1}/{sizeID2}/{ColorID}")]
etc.
Do I need to create end point for every tipe of search or is there a smarter way of doing it?
You should use the query params. You can add them via FromQuery attribute:
[HttpGet]
public IActionResult SearchProducts([FromQuery] int[] sizeIds, [FromQuery] int[] colorIds) {
}
You can replace int with string if you have string Ids.
For example, if you want to make a request with sizes 1 and 2, and color 3 and 4, the request would look like this: https://localhost:5001/your-endpoint-name?sizeIds=1&sizeIds=2&colorIds=3&colorIds=4
So query is a list of key=value url parameters after the ? separated by & sign
EDIT
You can easily query the database with the sql IN operator.
In EF Core, it would look something like this:
IQuaryable<Product> query = dbContext.Products;
if (sizeIds.Length > 0) {
query= query.Where(p => sizeIds.Contains(p.SizeId));
}
if (colorIds.Length > 0) {
query= query.Where(p => colorIds.Contains(p.ColorId));
}
List<Product> result = await query.ToListAsync();
It would be translated to the following SQL:
SELECT * FROM Products
WHERE Products.SizeId IN (1, 2) AND Products.ColorId IN (3, 4);
The problem is that I have nested classess
public class ProductBase
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<ProductVariant> Variants { get; set; } = new List<ProductVariant>();
public int BaseImageId { get; set; } = 0;
public string BaseImagePath { get; set; } = string.Empty;
}
public class ProductVariant
{
public int Id { get; set; }
public int Quantity { get; set; }
public int ProductBaseId { get; set; }
public int ProductSizeId { get; set; }
public ProductSize ProductSize { get; set; }
public int ProductColorId { get; set; }
public ProductColor ProductColor { get; set; }
public IEnumerable<ImageVariant> imageVariants { get; set; } = new List<ImageVariant>();
}
public class ProductSize
{
public int Id { get; set; }
public string Size { get; set; }
}
public class ProductColor
{
public int Id { get; set; }
public string Color { get; set; }
}
I am trying something like this
public async Task<IQueryable<Models.ProductBase>> SearchProducts(int[] SizeIds, int[] ColorIds )
{
IQueryable<Models.ProductBase> query = _dataContext.ProductBases
.Include(pb => pb.Variants).ThenInclude(v => v.ProductSize)
.Include(pb => pb.Variants).ThenInclude(v => v.ProductColor)
.Include(pb => pb.Variants).ThenInclude(v => v.imageVariants);
if(SizeIds.Length > 0)
{
query = query.Where(pb => SizeIds.Contains(pb.Variants.Any(pb.Variants.doesnotWork));
}
if(ColorIds.Length > 0)
{
query = query.Where(pb => ColorIds.Contains(pb.Variants.Contains(pb.Variants.doesNotWork)));
}
List<ProductBase> result = await query.ToListAsync();
}
Fixed , this is working now
public async Task<IEnumerable<Models.ProductBase>> SearchProducts(int[] SizeIds, int[] ColorIds )
{
IQueryable<Models.ProductBase> query = _dataContext.ProductBases
.Include(pb => pb.Variants).ThenInclude(v => v.ProductSize)
.Include(pb => pb.Variants).ThenInclude(v => v.ProductColor)
.Include(pb => pb.Variants).ThenInclude(v => v.imageVariants);
if(SizeIds.Length > 0)
{
query = query.Where(pb => pb.Variants.ToList().Any(v => SizeIds.Contains(v.ProductSizeId)));
}
if(ColorIds.Length > 0)
{
query = query.Where(pb => pb.Variants.ToList().Any(v => ColorIds.Contains(v.ProductColorId)));
}
List<Models.ProductBase> result = await query.ToListAsync();
return result;
}
I have this models
public class RoutingAttributeModel
{
public int Bus_No { get; set; }
public int Attribute_No { get; set; }
public string Attribute_Name { get; set; }
public string Status { get; set; }
public string Notes { get; set; }
}
public class AgentRoutingAttributeModel
{
public int Agent_No { get; set; }
public int Bus_No { get; set; }
public int Attribute_No { get; set; }
public string Attribute_Name { get; set; }
public string Status { get; set; }
}
List<RoutingAttributeModel> lstComplete = new List<RoutingAttributeModel>();
List<AgentRoutingAttributeModel> lstAssigned = new List<AgentRoutingAttributeModel>();
Filled this with some data
Is it possible to filter with Linq? I want to save in a new list the diferent content between lstComplete and lstAssigned
I was trying to join both lists but got stuck there
var results1 = from cl in lstComplete
join al in lstAssigned
on cl.Attribute_No equals al.Attribute_No
select cl;
you can use linq
as my understanding, you try to find linked by attribute_No records and have a list of not matching properties?
lstComplete.Add(new RoutingAttributeModel(){
Attribute_Name = "aaa",
Attribute_No = 1,
Bus_No = 1,
Notes = "",
Status = "status"
});
lstAssigned.Add(new AgentRoutingAttributeModel()
{
Attribute_No = 1,
Agent_No = 10,
Bus_No = 1,
Attribute_Name = "bbb",
Status = "status2"
});
var lst = lstComplete
.Join(lstAssigned,
complete => complete.Attribute_No,
assigned => assigned.Attribute_No,
(complete, assigned) => new { lstComplete = complete, lstAssigned = assigned })
.Select(s => new { s.lstComplete, s.lstAssigned})
.Where(w=>
w.lstAssigned.Attribute_Name != w.lstComplete.Attribute_Name
|| w.lstAssigned.Bus_No != w.lstComplete.Bus_No
)
.ToList()
.Dump();
so result would be
You could try the following query
var filteredList = lstComplete
.Where(x => !lstAssigned.Any(y => y.Attribute_No == x.Attribute_No));
I have the following LINQ statement:
var repoActivityRowsTest = appManager.GetRepository<ActivityRow>();
var activityRowsTest = await repoActivityRowsTest.Search(f => f.ExcelReport.uploadPhase == RPToolConstants.Phase_Planning, includeProperties: "PlanningInfo")
.Where(f => iso3Alpha3List.Contains(f.ExcelReport.countryOfficeIso3Alpha3))
.SelectMany(sm => sm.PlanningInfo).Select(s => new { s.Year, s.Count, s.ActivityRow.UnitCost })
.GroupBy(g=>new { g.Year }).Select(sg=>new { sg.Key.Year, Total = sg.Sum(sum => sum.UnitCost * sum.Count) })
.ToListAsync();
Which uses the repository pattern. The search function is the one below:
public IQueryable<TEntity> Search(Expression<Func<TEntity, bool>> filter = null,
string includeProperties = "", bool trackChanges = false)
{
IQueryable<TEntity> query = context.Set<TEntity>();
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty.Trim());
}
if (!trackChanges)
{
query = query.AsNoTracking();
}
return query;
}
When I inspect the command that arrives in SQL Server I see that the query is translated in the following SQL:
SELECT [a0].[Year], SUM([a1].[UnitCost] * CAST([a0].[Count] AS decimal(18,2))) AS [Total]
FROM [ActivityRows] AS [a]
INNER JOIN [ExcelReports] AS [e] ON [a].[ExcelReportId] = [e].[Id]
INNER JOIN [ActivityRowPlanningInfo] AS [a0] ON [a].[Id] = [a0].[ActivityRowId]
INNER JOIN [ActivityRows] AS [a1] ON [a0].[ActivityRowId] = [a1].[Id]
WHERE ([e].[uploadPhase] = N'planning')
AND [e].[countryOfficeIso3Alpha3] IN (N'AFG', N'DZA', N'AGO', N'ARM', N'BGD')
GROUP BY [a0].[Year]
It works perfectly, but why there is an inner join duplicated:
INNER JOIN [ActivityRows] AS [a1] ON [a0].[ActivityRowId] = [a1].[Id]
is a non-sense to me!
If I remove it from the SQL it works as before. Is there any issue in my LINQ query that causes this strange SQL?
here is the definition of the entities:
public class ActivityRow : Entity<int>
{
public string Description { get; set; }
public int ExcelReportId { get; set; }
[ForeignKey("ExcelReportId")]
public virtual ExcelReport ExcelReport { get; set; }
public int ActivitySubTypeId { get; set; }
[ForeignKey("ActivitySubTypeId")]
public virtual ActivitySubType ActivitySubType { get; set; }
public int? ActivityCategoryId { get; set; }
[ForeignKey("ActivityCategoryId")]
public virtual ActivityCategory ActivityCategory { get; set; }
public string ResponsibleEntity { get; set; }
[Column(TypeName = "decimal(18,2)")]
public decimal UnitCost { get; set; }
public string Notes { get; set; }
public virtual ICollection<ActivityRowReportingInfo> ReportingInfo { get; set; }
public virtual ICollection<ActivityRowPlanningInfo> PlanningInfo { get; set; }
}
public class ActivityRowPlanningInfo : Entity<int>
{
public int ActivityRowId { get; set; }
[ForeignKey("ActivityRowId")]
public virtual ActivityRow ActivityRow { get; set; }
public int Year { get; set; }
public int Quarter { get; set; }
public int Count { get; set; }
}
and here the definition of the relationships with fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//activities
modelBuilder.Entity<ActivityRow>()
.HasMany(b => b.ReportingInfo)
.WithOne(t => t.ActivityRow)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ActivityRow>()
.HasMany(b => b.PlanningInfo)
.WithOne(t => t.ActivityRow)
.OnDelete(DeleteBehavior.Cascade);
...etc.
}
Rewrite query via LINQ Query syntax and you can simplify your query with ease.
The following query do not create non wanted joins:
var repoActivityRowsTest = appManager.GetRepository<ActivityRow>();
var activityRows = repoActivityRowsTest
.Search(f => true);
var resultQuery =
from ar in activityRows
where
ar.ExcelReport.uploadPhase == RPToolConstants.Phase_Planning
&& iso3Alpha3List.Contains(ar.ExcelReport.countryOfficeIso3Alpha3)
from pi in ar.PlanningInfo
group new { ar, pi } by new { pi.Year } into g
select new
{
g.Key.Year,
Total = g.Sum(x => x.ar.UnitCost * x.pi.Count)
};
var result = await resultQuery.ToListAsync();
I am having a class like this.
public class CameraModel
{
public int JobId { get; set; }
public int ViewId { get; set; }
public Guid ViewGuid { get; set; }
public string Name { get; set; }
public int ViewNum { get; set; }
public int LayoutID { get; set; }
public List<CameraViewItemModel> CameraViewItems { get; set; }
}
The CameraViewItemModel class is like this:
public class CameraViewItemModel
{
public int JobID { get; set; }
public Guid ViewGuid { get; set; }
public int ViewID { get; set; }
public int CamNum { get; set; }
public Guid ChannelGuid { get; set; }
public string Name { get; set; }
public ActionType Action { get; set; }
}
Now, I am assigning the list of CameraViewItemModel like this:
// get all the cameramodel's
cameraModels = _unitOfWork.Context.CameraViews.Where(m => m.JobId == siteId)
.Select(m => new CameraModel
{
JobId = m.JobId,
ViewId = m.ViewId,
ViewGuid = m.ViewGuid,
Name = m.Name,
ViewNum = m.ViewNum,
LayoutID = m.LayoutId
}).ToList();
// get all the cameraviewitemmodels
cameraViewItemModels =
(from cameraView in _unitOfWork.Repository<CameraViews>().Get(x => x.JobId == siteId).Result
join cameraViewItem in _unitOfWork.Repository<CameraViewItems>().Get(x => x.JobId == siteId)
.Result on cameraView.ViewId equals cameraViewItem.ViewId into CameraViewItemResults
from cameraViewItemResult in CameraViewItemResults.DefaultIfEmpty()
join cameraChannel in _unitOfWork.Repository<CameraChannels>().Get(x => x.JobId == siteId)
.Result on (cameraViewItemResult == null ? new Guid() : cameraViewItemResult.ChannelGuid) equals cameraChannel.ChannelGuid into CameraChannelResults
from cameraChannelResult in CameraChannelResults.DefaultIfEmpty()
select new CameraViewItemModel
{
JobID = cameraView.JobId,
ViewID = cameraView.ViewId,
ViewGuid = cameraView.ViewGuid,
CamNum = cameraViewItemResult.CamNum,
ChannelGuid = cameraChannelResult.ChannelGuid,
Name = cameraChannelResult.Name
}).ToList();
// then do a 'join' on JobId, ViewId and ViewGuid and assign the list of cameraviewitemmodels to cameraModels.
foreach (var cameraModel in cameraModels)
{
cameraModel.CameraViewItems = (from cameraViewItem in cameraViewItemModels
where cameraModel.JobId == cameraViewItem.JobID
&& cameraModel.ViewId == cameraViewItem.ViewID
&& cameraModel.ViewGuid == cameraViewItem.ViewGuid
select cameraViewItem).ToList();
}
return cameraModels;
There are three tables in database:
CameraViews, CameraViewItems, CameraChannels.
CameraViews is the main table. It is left joined with CameraViewItems and CameraChannels to get the desired result. There may not be any data in CameraViewItems and CameraChannels for a corresponding CameraView.
Is it possible to assign the list of CameraViewItemModels to CameraModels in a single linq statement.
Here is a simple way to add values to a sub list, dunno if this is what you mean. You can keep selecting sub lists if that is necessary.
var parent_lst = new List<List<string>>(); // Root/parent list that contains the other lists
var sub_lst = new List<string>(); // Sub list with values
var selected_parent_lst = parent_lst[0]; // Here I select sub list, in this case by list index
selected_parent_lst.Add("My new value"); // And here I add the new value
i am new to lambda expression so i try to solve one problem .but i can't. so can anyone suggest solution for this.
i have one class customer. inside i created another 3 class and create observable collection for 3 classes.i create one observable collection for this customer
ObservableCollection<Customer> customer2;
public class Customer
{
public string CusName { get; set; }
public int CusAge { get; set; }
public ObservableCollection<Bankdetails> bankdetails;
public ObservableCollection<order> orderlist;
public ObservableCollection<orderdetails> orderdetailslist;
public class Bankdetails
{
public string Bankaccno { get; set; }
public string bankname { get; set; }
public int bankid { get; set; }
}
public class order
{
public string ordername { get; set; }
public string orderid { get; set; }
}
public class orderdetails
{
public string orderid { get; set; }
public string itemname { get; set; }
public int itemqty { get; set; }
}
}
i write one linq query for getting values from customer2.anyhow its working .like this i tried to write one lambda query but i can't.
here i adding some values to observable collection.
customer2 = new ObservableCollection<Customer>
{
new Customer()
{
CusName="nixon",CusAge=24,
bankdetails=new ObservableCollection<Customer.Bankdetails>
{
new Customer.Bankdetails()
{
bankid=12,bankname="axis",Bankaccno="09876534"
}
},
orderlist=new ObservableCollection<Customer.order>
{
new Customer.order
{
orderid="Od123",ordername="Express"
}
},
orderdetailslist=new ObservableCollection<Customer.orderdetails>
{
new Customer.orderdetails
{
orderid="Od123",itemname="cpu",itemqty=5
}
}
}
};
this is my linq query
var customer1 = from cus in customer2
from bank in cus.bankdetails
from ord in cus.orderlist
from orddet in cus.orderdetailslist
where ord.orderid == orddet.orderid
select new
{
cus.CusAge,cus.CusName,
bank.Bankaccno,bank.bankid,bank.bankname,
ord.ordername,
orddet.itemname,orddet.itemqty
};
then what will be the lambda query.pls anyone suggest .
Matt's solution extended with the where from the question would be:
var xxx = customer2.SelectMany(cus =>
cus.bankdetails.SelectMany(bank =>
cus.orderlist.SelectMany(ord =>
cus.orderdetailslist.Where(orddet => orddet.orderid == ord.orderid)
.Select(orddet => new
{
cus.CusAge,
cus.CusName,
bank.Bankaccno,
bank.bankname,
orddet.itemname,
orddet.itemqty
}
)
)
)
);
var xxx = customer2.SelectMany(cus =>
cus.bankdetails.SelectMany(bank =>
cus.orderlist.SelectMany(ord =>
cus.orderdetailslist.Select(orddet => new
{
cus.CusAge,
cus.CusName,
bank.Bankaccno,
bank.bankname,
orddet.itemname,
orddet.itemqty
}
)
)
)
);