Join few tables without repeat same data - c#

I have got two tables,
Feedback Table and Steps Table. Each feedback has multiple Steps. If i use LINQ with join method it returnd same number of step items (obj) in FeedbackViewModel and each contains only 1 single step. I mean ery similar to SQL return.
FeedbackViewModel[0]{FeedbackID = 1,FeedbackName="NameA", Steps{Step1}}
FeedbackViewModel[1]{FeedbackID = 1,FeedbackName="NameA", Steps{Step2}}
FeedbackViewModel[2]{FeedbackID = 1,FeedbackName="NameA", Steps{Step3}}
BUT I only want one FeedbackViewModel object which contains many Steps.
FeedbackViewModel[0]{FeedbackID = 1,FeedbackName="NameA", Steps{Step1, Step2, Step 3 etc..}
I can do it by foreach Loop but it doesn't look professional.
Thanks for help
public class Feedback
{
public int FeedbackID { get; set; }
public string FeedbackName { get; set; }
}
public class StepModel
{
public int StepID { get; set; }
public int FeedbackID { get; set; } = 0;
public int StepNumber { get; set; }
public string StepDetail { get; set; }
public virtual Feedback FeedBack { get; set; }
}
public class FeedbackViewMODEL
{
public int FeedbackID { get; set; }
public string FeedbackName { get; set; }
public List<StepModel> Steps { get; set; }
}
var quote = from feed in FeedBacks
join stp in StePs on feed.FeedbackID equals stp.FeedbackID
where feed.FeedbackID == id
select new FeedbackViewMODEL
{
Step = stp;
}

Using the LINQ group join will combine all the matching steps:
var quote = from feed in FeedBacks
where feed.FeedbackID == id
join stp in StePs on feed.FeedbackID equals stp.FeedbackID into stpj
select new FeedbackViewMODEL {
FeedbackID = feed.FeedbackID,
FeedbackName = feed.FeedbackName,
Steps = stpj.ToList()
};

I think you should left outer join.
from feed in Feedbacks
join stp in steps on feed.FeedbackID equal stp.FeedbackID into result
where feed.FeedBackID == id and result.DefaultIfEmpty()

Related

EF Core recursive folder structure serialized incorrectly

I'm trying to utilize EF Core to execute a Raw Sql statement and "serialize" it back to a recursive object structure.
My data looks like this
Folders -
Requests -
//Folder.cs
[Table("PsRequestFolder")]
public class PsRequestFolder : IWorkSpaceNode
{
[Key]
public int FolderId { get; set; }
public string FolderName = "";
public PsWorkSpace? FolderWorkSpace { get; set; }
public PsRequestFolder? ParentFolder { get; set; }
public List<PsHttpRequest> Requests { get; set; } = new List<PsHttpRequest>();
public List<PsRequestFolder> Folders { get; set; } = new List<PsRequestFolder>();
...
// Request.cs
[Table("PsHttpRequest")]
public class PsHttpRequest : IWorkSpaceNode
{
[Key]
public int RequestId { get; set; }
public string Url = "";
public string RequestName = "";
public PsWorkSpace RequestWorkSpace{ get; set; }
public int PsRequestFolderFolderId { get; set; }
public PsRequestFolder ContainingFolder { get; set; }
...
I'm executing this sql statement by calling FromSqlRaw
ws.RequestFolders = await context.RequestFolders.FromSqlRaw(
#"
WITH cte_ws AS (
SELECT
FolderId,
FolderName,
ParentFolderFolderId,
FolderWorkSpaceWorkSpaceId,
p1.*
FROM PsRequestFolder
LEFT JOIN PsHttpRequest AS p1 ON PsRequestFolder.FolderId = p1.PsRequestFolderFolderId
WHERE ParentFolderFolderId IS NULL
UNION ALL
SELECT
e.FolderId,
e.FolderName,
e.ParentFolderFolderId,
e.FolderWorkSpaceWorkSpaceId,
p1.*
FROM PsRequestFolder as e
LEFT JOIN PsHttpRequest AS p1 ON e.FolderId = p1.PsRequestFolderFolderId
INNER JOIN cte_ws o
on o.FolderId= e.ParentFolderFolderId
)
SELECT * FROM cte_ws;
"
).ToListAsync();
I would expect to get back something that looks like
One
RequestOne
RequestTwo
Two
Six
Eight
Nine
RequestThree
Seven
Three
Four
Five
Instead I get something like this back
Not sure why some of the things that should be nested underneath others aren't being nested as well as why the requests under folder Nine don't come through at all, as well folder Nine and some others being written twice to the list
The raw sql output just running it against the db is -
Any assistance is greatly appreciated!

Debugging Call Stack Exceeded C# Backend

I have a get route a that is going to get data of wells and well tests, when I execute the call on swagger, it will take awhile and then give me a call stack error. My problem is I cant figure out how to get a log or idea of where this is happening. The best I have been able to do so far is use point breaks at every step to see how far it gets. I've gotten to the controller route so I know that its grabbing the data just fine, my understanding is that it now has the data, and should use the view model to match and display the data. I have gone through about 100 data samples in the view model and it seems fine but there is 2400 units, all with 5 arrays inside of them. However it will simply error out with no message. Any ideas of whats going on or how to debug this? Is there a way in VS Code so see a better log of something like this or another tool that will do that will help in this situation?
** Service Code: **
public async Task<IEnumerable<SapDispatchViewModel>> GetDispatchDeliveryForSap()
{
var result = await _dispatchRepo.GetDispatchDeliveryForSap(TenantId);
var view = new List<SapDispatchViewModel>();
foreach (SapDispatch row in result)
{
var sapView = _mapper.Map<SapDispatch, SapDispatchViewModel>(row);
var items = await _dispatchItemRepo.GetDispatchItemsByTruckForSap(row.DispatchTruckId);
var viewItems = _mapper.Map<IEnumerable<SapDispatchItem>, IEnumerable<SapDispatchItemViewModel>>(items);
sapView.Items = viewItems;
view.Add(sapView);
}
return view;
}
** It calls this GetDispatchDeliveryForSap first: **
public async Task<IEnumerable<SapDispatch>> GetDispatchDeliveryForSap(string TenantId)
{
string deliveryType = "Delivery";
//resort to raw SQL to assist with performance improvements
FormattableString sql = $#"
WITH cte_latestStatus AS
( SELECT * FROM (
SELECT
s.TenantId,
s.DispatchId,
s.DispatchHeaderId,
s.RequestedArrival,
s.EstimatedArrival,
s.Status,
u.FirstName + ' ' + u.LastName UserName,
s.CreateDate StatusChangeDate,
row_number() over(partition by DispatchHeaderId order by CreateDate desc) as rn
FROM
DispatchStatus s
JOIN AspNetUsers u on s.CreateUserId = u.Id
) t
WHERE t.rn = 1
)
select w.wellid,
w.wellname,
wo.ErpId,
wc.ContractorName + ' ' + w.RigNumber Rig,
w.CountyParish County,
w.State,
d.type DispatchType,
u.LastName + ',' + u.FirstName OrderedBy,
ds.RequestedArrival RequestedDate,
dt.DriverName,
dt.SwamperName,
dt.TicketNumber,
dt.DispatchTruckId
from well w
join Dispatch d on w.wellid = d.DestinationWellId
join cte_latestStatus ds on d.DispatchId = ds.DispatchId and d.HeaderId = ds.DispatchHeaderId
join DispatchTruck dt on d.DispatchId = dt.DispatchId
join AspNetUsers u on d.CreateUserId = u.Id
left join WellContractorRef wcr on w.WellId = wcr.WellId
left join Contractor wc on wcr.ContractorId = wc.ContractorId
left join WellOperatorRef wor on w.WellId = wor.WellId
left join Operator wo on wor.OperatorId = wo.OperatorId
--join DispatchItem di on dt.DispatchTruckId = di.DispatchTruckId
where d.TenantId = {TenantId}
and d.type = {deliveryType}
and (ds.Status = 'Completed' or dt.status = 'Completed')
order by w.wellname"
;
var result = await context.SapDispatches.FromSqlInterpolated(sql).AsNoTracking().ToListAsync();
return result;
}
}
}
** Then maps via the view model to create the list: **
namespace Mudman.Model.ViewModels
{
public class SapDispatchViewModel
{
public string WellId { get; set; }
public string WellName { get; set; }
public string ErpId { get; set; }
public string Rig { get; set; }
public string County { get; set; }
public string State { get; set; }
public string DispatchType { get; set; }
public string OrderedBy { get; set; }
public DateTime? RequestedDate { get; set; }
public string DriverName { get; set; }
public string SwamperName { get; set; }
public long? TicketNumber { get; set; }
public IEnumerable<SapDispatchItemViewModel> Items { get; set; }
}
public class SapDispatchItemViewModel
{
public string ErpId { get; set; }
public Decimal? Price { get; set; }
public Decimal? Quantity { get; set; }
public string Size { get; set; }
public string Unit { get; set; }
}
}
** From there, it runs the foreach on the GetDispatchItemsForTruckSap: **
public async Task<IEnumerable<SapDispatchItem>> GetDispatchItemsByTruckForSap(string dispatchTruckId)
{
//resort to raw SQL to assist with performance improvements
FormattableString sql = $#"
WITH cte as (
SELECT
COALESCE(ProductId, ExpenseId) AS SalesItemID,
Price,
Quantity
FROM DispatchItem
WHERE DispatchTruckId = {dispatchTruckId}
)
SELECT si.ErpId,
cte.Price,
cte.Quantity,
si.Size,
si.Unit
FROM SalesItem si
INNER JOIN cte on cte.SalesItemID = si.SalesItemId"
;
var result = await context.SapDispatchItems.FromSqlInterpolated(sql).AsNoTracking().ToListAsync();
return result;
}
}
}
** Maps with the Item View Model: **
public class SapDispatchItemViewModel
{
public string ErpId { get; set; }
public Decimal? Price { get; set; }
public Decimal? Quantity { get; set; }
public string Size { get; set; }
public string Unit { get; set; }
}
}
** Then it will hit the return and thats where it will error out.
Also, here is what the callstack is looking like when you hit that return.
Try turning on Break When Thrown on Common Language Runtime Exceptions and it should break at the error:

Sql query combining results

I have the following:
void Main()
{
// I need to get the below query, but with the newest comment date as well of each post.
// Gives me all the posts by the member
var member_id = 1139;
var query = (from posts in Ctm_Forum_Posts
join category in Ctm_Forum_Categories on posts.FK_Categori_ID equals category.Id
where posts.Archieved == false
&& posts.Deleted == false
&& posts.FK_Member_ID == member_id
select new ForumPostModel(){
Id = posts.Id,
BodyText = posts.BodyText,
Summary = posts.Summary,
Title = posts.Title,
Created = posts.Created,
Updated = posts.Updated,
CategoryName = category.Title,
});
// this gives the newest comment date (registration_timestamp) from a specific post with id = 1
var NewestCommentDate = (from comments in Ctm_Comments
join posts in Ctm_Forum_Posts on comments.Page_ID equals posts.Id
where posts.Id == 1 && comments.Deleted == false
orderby comments.Reqistration_timestamp descending
select comments.Reqistration_timestamp).FirstOrDefault();
}
// Model of the result.
public class ForumPostModel{
public int Id { get; set; }
public string Title { get; set; }
public string BodyText { get; set; }
public string Summary { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public string CategoryName { get; set; }
public DateTime LatestCommentTime { get; set; }
}
Right now the two queries work, but my problem is the performance sucks,, so im trying to combine the results into a single query, so my load time will be much less.
I tried searching about unions, but have been unable to figure it out.

Linq to Entities - how to delete many records with inner join

I have two classes:
public class Address
{
[Key]
public int ID_Address { get; set; }
public string URL { get; set; }
and:
public class Category_Address
{
[Key]
public int ID_Category_Address { get; set; }
public int ID_Category { get; set; }
public virtual Category Category { get; set; }
public int ID_Address { get; set; }
public virtual Address Address { get; set; }
}
I want to select ID_Address from Address table where URL = "anytextIwrite" and delete every record in Category_Address having the same ID_Address that I selected. How can I do that ?
Now I wrote this with a few assumptions. Obviously you'll want to use your own database names and whatever group names you want for them, but this is a fairly simple LINQ statement and join, followed by a foreach that should do what you want.
var toDelete = from addr in addresses
join cate in category_addresses on addr.id_address equals cate.id_address
where addr.url = "anytextIwrite"
select cate;
foreach (var record in toDelete) {
db.category_addresses.DeleteOnSumbit(record);
}
If still active, then try smth like this:
using (var context = new YourDbModel())
{
var deletingAddresses = context.Category_Addresses
.Where(ca => ca.Address.URL == "anytextIwrite")
.Select(ca => ca.Address)
.ToArray();
foreach(var address in deletingAddresses)
{
context.Category_Addresses.Attach(address);
var entry = context.Entry(address);
entry.State = EntityState.Deleted;
}
context.SaveChanges();
}
It worked for me, so maybe you need the same.

Add data to a list insde a list where item in the prerent list

I would like to select a where statement that adds items to a list where only product codes match. I have it so it gets all of the products sold in the sale but I would like there were statement to get only products in this sale.
PS: This is really hard to explain
Model
public class userSales
{
public string Name { get; set; }
public string UserName { get; set; }
public int Sale_Id { get; set; }
public int CostumerID { get; set; }
public string Sale_Date { get; set; }
public string Paid { get; set; }
public Nullable<int> Sale_Cost { get; set; }
public string Discount_Code { get; set; }
public List<SaleProduct> saleProductsList { get; set; }
}
public class SaleProduct
{
public int SaleID { get; set; }
public string ProductCode { get; set; }
public int ProductCount { get; set; }
public string Image_Path { get; set; }
public string Shoot_Date { get; set; }
public string Shoot_Info { get; set; }
}
Linq statement where I'm having trouble:
var test = (from _ClientData in db.ClientDatas
join _salesInfo in db.Sales_Infoes
on _ClientData.CostumerID
equals _salesInfo.CostumerID
where _ClientData.UserName == _userName
select new userSales()
{
CostumerID = _ClientData.CostumerID,
Name = _ClientData.Name,
UserName = _ClientData.UserName,
Sale_Id = _salesInfo.Sale_Id, // This is the item i would like to use in my were statement
Sale_Date = _salesInfo.Sale_Date,
Sale_Cost = _salesInfo.Sale_Cost,
Discount_Code = _salesInfo.Discount_Code,
Paid = _salesInfo.Paid,
// Problem here
saleProductsList = db.SaleProducts.Where()
}).ToList();
Got to this based on the answer:
var reult = db.ClientDatas.Where(a => a.UserName == _userName)
.Join(db.Sales_Infoes,
a => a.CostumerID,
b => b.CostumerID,
(a, b) => new userSales()
{
CostumerID = a.CostumerID,
Discount_Code = b.Discount_Code,
Sale_Cost = b.Sale_Cost,
Sale_Id= b.Sale_Id,
Name = a.Name,
Sale_Date = b.Sale_Date,
UserName = a.UserName,
Paid = b.Paid,
saleProductsList = db.SaleProducts.Where(c => c.SaleID == b.Sale_Id).ToList()
}).ToList();
You're not looking for a where, you're looking for a join. Where filters the results on a single table, join intersects two tables which is actually what you want here.
var result = db.Sales_Infoes.Where(x => x.UserName == _userName)
.Join(db.ClientDatas,
x => x.Sale_Id,
y => y.Sale_id,
(x, y) => new userSales() {
// x is SalesInfo obj y is ClientDatas obj do assignement here
Name = y.Name,
Sale_Date = y.Sale_date
}).ToList();
Just fyi I haven't had a chance to test that but it's the basic idea. You don't need a select like in your statement because the last argument I'm passing into join is the lambda (x, y) => ... in that case x and y are the current row from each table (that we've gotten from applying our where to the user sales table then joining those results into the salesproduct table) so whatever projections you want to do occur there. The other two method args above that are the telling join which fields to compare, it's the 'key selector' lambda expression for each table.

Categories

Resources