Referring to an entity in another context - Entity Framework - c#

I am trying to use Any method to achieve exists functionality in Entity Framework but when I refer to another entity in the current context, I get an error
The name OrderNumber does not exists in current context
How can I fix this?
var ordResults = from ord in orders
select new Order
{
OrderId = ord.OrderId,
WarehouseId = ord.WarehouseId,
OrderNumber = ord.OrderNumber,
CustomerPoNumber = ord.CustomerPoNumber,
ShipToCode = ord.ShipToCode,
ShipToName = ord.ShipToName,
OrderDate = ord.OrderDate,
PromisedDate = ord.PromisedDate,
if (aadContext.TransactionLogs.Any(t => t.ControlNumber == **OrderNumber** && t.TransactionType == "670")) {"C"} else {"D"},
ord.Status,
ord.CustomerName,
}

You could also try something like this.
I still dont understand what {"C"} else {"D"} if its condition you could add that like
t.somevalue=="C" || t.SomeValue=="E"
var ordResults = from ord in orders
let someValue = aadContext.TransactionLogs.Any(t => t.ControlNumber == ord.OrderNumber && t.TransactionType == "670"))
select new Order
{
OrderId = ord.OrderId,
WarehouseId = ord.WarehouseId,
OrderNumber = ord.OrderNumber,
CustomerPoNumber = ord.CustomerPoNumber,
ShipToCode = ord.ShipToCode,
ShipToName = ord.ShipToName,
OrderDate = ord.OrderDate,
PromisedDate = ord.PromisedDate,
DoYourValue = SomeValue
ord.Status,
ord.CustomerName,
}

Related

Add conditions after muilti join LINQ expression

I've got a Linq query with 5 joins/tables, with predefined conditions.
Because I need the query multiple times I've created a function which returns the default LINQ query as an IQueryable.
public static IQueryable<MroomLinqModel> GetDefaultQuery(CustomerContext CustomerCtx)
{
var Mrooms = (from mr in CustomerCtx.Mrooms
join m in CustomerCtx.Moves on mr.MoveId equals m.MoveId
join mg in CustomerCtx.mgroup on m.MgroupId equals mg.MgroupId
join s in CustomerCtx.Status on m.StatusId equals s.StatusId
join rt in CustomerCtx.Roomtypes on mr.RoomtypeId equals rt.Key
join g in CustomerCtx.Guests on m.Mgroup.GuestId equals g.GuestId
where
Math.Abs(mg.Status) != (int)IResStatus.InComplete &&
s.Visible
select new MroomLinqModel
{
OpenDepositPayments = mg.DepositPayments.Any(dp => !dp.Paid),
RoomHidden = (mr.RoomId == null ? true : mr.Room.Hidden),
StatusVisible = s.Visible,
MroomId = mr.MroomId,
MoveId = m.MoveId,
MgroupId = mg.MgroupId,
StatusId = s.StatusId,
StatusFlags = s.Flags,
BackgroundColor = s.Background_Argb,
TextColor = s.Foreground_Argb,
PersonCount = m.Movegroups.Sum(m => m.PersonCount),
MoveCount = mg.Moves.Count(),
RoomId = mr.RoomId,
PMSMroomId = mr.PMS_Id,
PMSMoveId = m.PMS_Id,
PMSMgroupId = mg.MgroupId_Casablanca,
From = mr.From,
Until = mr.Until,
EditableState = m.EditableState,
MroomStatus = mr.Status,
RoomtypeUsage = mr.Roomtype.Usage,
BookingReference = mg.ReferenceNumber,
Guest = g
});
return Mrooms;
}
Now I would like to add some conditions afterwards like:
Query = GetDefaultQuery.Where(q => !q.RoomHidden && q.From <= dtLoadEnd && dtLoadStart <= q.Until);
Query = Query.Where(q => q.RoomtypeUsage == RoomtypeUsageType.Roomplan);
This works fine but takes a lot more time to execute as if I add all conditions directly to the first LINQ query.
How do I form the query to access the original tables and generate a fast query?
Since all your condition match the table where you are joining the other tables you can add the conditions before your multi join, I got 2 suggestions for you, either create another method that filters your conditions predefined or add to your method optional filters like so :
public static IQueryable<MroomLinqModel> GetDefaultQuery(CustomerContext CustomerCtx, bool? roomHidden, DateTime? dtLoadEnd
/* you can add more parameters but for demonstrations purposes i'm only describing this 2*/)
{
var query = CustomerCtx.Mrooms;
if(roomHidden.HasValue)
{
query = query.Where( q=>q.From == roomHidden.Value)
}
if(dtLoadEnd .HasValue)
{
query = query.Where( q=>q.RoomHidden <= dtLoadEnd.Value)
}
// you can add more conditions
var Mrooms = (from query
join m in CustomerCtx.Moves on mr.MoveId equals m.MoveId
join mg in CustomerCtx.mgroup on m.MgroupId equals mg.MgroupId
join s in CustomerCtx.Status on m.StatusId equals s.StatusId
join rt in CustomerCtx.Roomtypes on mr.RoomtypeId equals rt.Key
join g in CustomerCtx.Guests on m.Mgroup.GuestId equals g.GuestId
where
Math.Abs(mg.Status) != (int)IResStatus.InComplete &&
s.Visible
select new MroomLinqModel
{
OpenDepositPayments = mg.DepositPayments.Any(dp => !dp.Paid),
RoomHidden = (mr.RoomId == null ? true : mr.Room.Hidden),
StatusVisible = s.Visible,
MroomId = mr.MroomId,
MoveId = m.MoveId,
MgroupId = mg.MgroupId,
StatusId = s.StatusId,
StatusFlags = s.Flags,
BackgroundColor = s.Background_Argb,
TextColor = s.Foreground_Argb,
PersonCount = m.Movegroups.Sum(m => m.PersonCount),
MoveCount = mg.Moves.Count(),
RoomId = mr.RoomId,
PMSMroomId = mr.PMS_Id,
PMSMoveId = m.PMS_Id,
PMSMgroupId = mg.MgroupId_Casablanca,
From = mr.From,
Until = mr.Until,
EditableState = m.EditableState,
MroomStatus = mr.Status,
RoomtypeUsage = mr.Roomtype.Usage,
BookingReference = mg.ReferenceNumber,
Guest = g
});
return Mrooms;
}
expecting that CustomerCtx.Mrooms is a DbSet<Mroom> and that Mroom looks like :
public class Mroom {
public int MroomId {get; set;}
[ForeignKey("Move")]
public int MoveId {get; set;} //FK
public virtual Move Move {get; set;} //Navigation property
//configuration may be needed cf annotation
[ForeignKey("Status")]
public int StatusId {get; set;}
public virtual Status Status {get; set;}
//... Mgroup, Guest, ...
}
If it is not the case I suggest you yo refactor your code to use navigation properties.
Then you may use PredicateBuilder of linqkit as follow:
public static IQueryable<MroomLinqModel> GetDefaultQuery(CustomerContext CustomerCtx, Expression<Function<Mroom, bool>> q)
{
Expression<Function<Mroom, bool>> w = PredicateBuilder.New<Mroom>
(s => Math.Abs(s.Mgroup.Status) != (int)IResStatus.InComplete &&
s.Visible);
w = w.And(q);
return
CustomerCtx.Mrooms.
Where(w.Expand()).
Select( x => new MroomLinqModel
{
OpenDepositPayments = x.Mgroup.DepositPayments.Any(dp => !dp.Paid),
RoomHidden = (x.RoomId == null ? true : x.Room.Hidden),
StatusVisible = x.Status.Visible,
MroomId = x.MroomId,
MoveId = x.MoveId,
MgroupId = x.MgroupId,
StatusId = x.Status.StatusId,
StatusFlags = x.Status.Flags,
BackgroundColor = x.Status.Background_Argb,
TextColor = x.Status.Foreground_Argb,
PersonCount = x.Move.Movegroups.Sum(m => m.PersonCount),
MoveCount = x.Mgroup.Moves.Count(),
RoomId = x.RoomId,
PMSMroomId = x.PMS_Id,
PMSMoveId = x.Move.PMS_Id,
PMSMgroupId = x.Mgroup.MgroupId_Casablanca,
From = x.From,
Until = x.Until,
EditableState = x.Move.EditableState,
MroomStatus = x.Move.Status,
RoomtypeUsage = mr.Roomtype.Usage,
BookingReference = x.Mgroup.ReferenceNumber,
Guest = x.Guest
}
);
}
and use it as:
GetDefaultQuery(ctx, q => !q.RoomHidden && q.From <= dtLoadEnd && dtLoadStart <= q.Until);
and or
Expression<Function<Mroom, bool>> w = PredicateBuilder.New<Mroom>(q =>
!q.RoomHidden && q.From <= dtLoadEnd && dtLoadStart <= q.Until);
//some logic
w = w.And(q => q.RoomtypeUsage == RoomtypeUsageType.Roomplan);
GetDefaultQuery(ctx, w);

Comparing tables in the controller

I have 2 tables, one is Orders, that holds the address/list of products, and a returnDetails table. This is used when a user wants to return an Order. The returnDetails has the OrderId that is being returned.
How do I display a message only on the orders that have a return with the same OrderID? Currently only "processing" is displayed.
I used a view model to display the Orders with products.
Here is what I tried but I haven't got it working yet:
[Authorize]
public ActionResult Index(string date)
{
string currentUser = this.User.Identity.GetUserName();
List<T_shirt_Company_v3.ViewModels.MyOrdersViewModel> list = (from o in new TshirtStoreDB().Orders
.Where(o => o.Username == currentUser)
.OrderByDescending(o => o.OrderDate)
.Select(o => new MyOrdersViewModel()
{
OrderId = o.OrderId,
Address = o.Address,
FirstName = o.FirstName,
LastName = o.LastName,
City = o.City,
OrderDate = o.OrderDate,
PostalCode = o.PostalCode,
Total = o.Total,
HasBeenShipped = o.HasBeenShipped,
PostageList = o.PostageList,
Details = (from d in o.OrderDetails
select new MyOrderDetails
{
Colour = d.Product.Colour,
Quantity = d.Quantity,
Title = d.Product.Title,
UnitPrice = d.UnitPrice
}).ToList()
}).ToList() select o).ToList();
if (date != null)
{
DateTime today = DateTime.Now.AddDays(Convert.ToInt32(date) * -1);
return View(list.Where(x => x.OrderDate >= today).ToList());
}
//WORKING ON
var returnstats = db.Orders.Where(x => x.OrderId == x.returnDetails.OrderId).ToList();
if (returnstats != null)
{
ViewBag.returnstats = "Returning Item";
}
else
{
ViewBag.returnstats = "processing";
}
return View(list);
}
I think the problem is that you are not clear about what is the business logic for 'returnstats'. From the values of this variable you provide, it looks like it's for a SINGLE order instead of an order list. Here is a modified Index method for your reference:
[Authorize]
public ActionResult Index(Int32? days)
{
string currentUser = this.User.Identity.GetUserName();
var orders = db.Orders //assume db is something inherited from DbContext
.Where(o => o.UserName == currentUser)
.OrderByDescending(o => o.OrderDate);
if (days.HasValue)
{
var startingDate = DateTime.Now.AddDays((-1) * days.Value);
orders = orders.Where(o => o.OrderDate >= startingDate);
}
var orderList = orders.ToList();
var returnOrderIds = db.ReturnDetails.Select(detail => detail.OrderId).ToList();
//here you will need to check with business analyst staff what is the rule for 'returnstats'.
//The business logic here is that if there is at least one order whose OrderId is in the ReturnDetails
//the 'returnstats' is 'Returning Item' otherwise it's 'Processing'.
if (orderList.Any(o => returnOrderIds.Contains(o.OrderId))
{
ViewBag.returnstats = "Returning Item";
} else
{
ViewBag.returnstats = "processing";
}
var viewModels = ...//the source code to create Order ViewModel with variable orderList
return View(viewModels);
}

Eager loading of joined properties

I am looking for a way to eager load some properties of an joined table.
The situation is as following:
I am making an class library which and web service will include.
In this library there must be a method which will return all devices with a specialized set of the device values.
In these device values there is a reference to the option of that value (and some other entities). I am returning a list since i don't want my connection to be open when i give the service the results.
The following problem occurs:
THe web service is trying to get to the option of a device value but get an exception because its not loaded yet and ef tries to lazy load it.
I know i need to eager load these references, but that's where y problem is.
i have the following linq expression:
db.Devices.Where(d => (d.Online)).Include("Pictures").Include("Brand").GroupJoin(
db.DeviceValues.Where(dv => (((((((((((((
dv.OptionId == new Guid("017AE564-742D-4389-91F8-805A58C77240")) ||
(dv.OptionId == new Guid("4BDD30A6-FBD7-4FFE-A4BA-F25BE2BE4586"))) ||
(dv.OptionId == new Guid("C6063BA6-231A-424A-92F4-A64BB1BABB7D"))) ||
(dv.OptionId == new Guid("57BD5C0B-5981-48EB-AE71-A52703FCA0CF"))) ||
(dv.OptionId == new Guid("A920ED14-8BB4-4097-B3CF-2DE7C79F34DD"))) ||
(dv.OptionId == new Guid("D29C64EF-69E1-46BB-909B-9B330031A493"))) ||
(dv.OptionId == new Guid("C3357431-5F4A-40F4-8FDD-1480E2F83D38"))) ||
(dv.OptionId == new Guid("7CB17F5E-03D3-40E9-A415-3D07BB9A1693"))) ||
(dv.OptionId == new Guid("D9FCE982-DD66-4FF3-A2A9-5BC84D2BFFF1"))) ||
(dv.OptionId == new Guid("0E008C9E-A306-E411-AB50-0050569C157F"))) ||
(dv.OptionId == new Guid("57EF5C9D-CE4D-40D4-93FE-FE3A2A9A4BDA"))) ||
(dv.OptionId == new Guid("085DE743-A18D-44A6-ACDE-EA5102290F48"))) ||
(dv.OptionId == new Guid("64A6279A-AB07-41C7-8E6D-3397FBC64FF3")))
), d => d.Id, dv => dv.DeviceId, (d, deviceValues) => new DeviceModel
{
Id = d.Id,
DeviceValues = deviceValues.ToList(),
Brand = d.Brand,
BrandId = d.BrandId,
Created = d.Created,
DeviceCategorie = d.DeviceCategorie,
Ean = d.Ean,
DeviceType = d.DeviceType,
IntroductionDate = d.IntroductionDate,
Moderator = d.Moderator,
Modified = d.Modified,
Name = d.Name,
Online = d.Online,
Pictures = d.Pictures,
SubDeviceFrom = d.SubDeviceFrom,
SubDeviceFromId = d.SubDeviceFromId,
dealId = d.dealId,
display = d.display,
oldId = d.oldId
}).ToList();
I tried to put my includes before the group join:
db.Devices.Where(d => (d.Online)).Include("Pictures").Include("Brand").Include("Option").GroupJoin
And:
db.Devices.Where(d => (d.Online)).Include("Pictures").Include("Brand").Include("DeviceValues.Option").GroupJoin
and i tried to put them after the select
new DeviceModel
{
Id = d.Id,
DeviceValues = deviceValues.ToList(),
Brand = d.Brand,
BrandId = d.BrandId,
Created = d.Created,
DeviceCategorie = d.DeviceCategorie,
Ean = d.Ean,
DeviceType = d.DeviceType,
IntroductionDate = d.IntroductionDate,
Moderator = d.Moderator,
Modified = d.Modified,
Name = d.Name,
Online = d.Online,
Pictures = d.Pictures,
SubDeviceFrom = d.SubDeviceFrom,
SubDeviceFromId = d.SubDeviceFromId,
dealId = d.dealId,
display = d.display,
oldId = d.oldId
}).Include("Option")
And:
new DeviceModel
{
Id = d.Id,
DeviceValues = deviceValues.ToList(),
Brand = d.Brand,
BrandId = d.BrandId,
Created = d.Created,
DeviceCategorie = d.DeviceCategorie,
Ean = d.Ean,
DeviceType = d.DeviceType,
IntroductionDate = d.IntroductionDate,
Moderator = d.Moderator,
Modified = d.Modified,
Name = d.Name,
Online = d.Online,
Pictures = d.Pictures,
SubDeviceFrom = d.SubDeviceFrom,
SubDeviceFromId = d.SubDeviceFromId,
dealId = d.dealId,
display = d.display,
oldId = d.oldId
}).Include("DeviceValues.Option")
Any help is much appreciated
You can force evaluation by calling toList() after your .Include() calls.
Like this:
db.Devices.Where(d => (d.Online)).Include("Pictures").Include("Brand").Include("Option").toList().GroupJoin
db.Devices.Where(d => (d.Online)).Include("Pictures").Include("Brand").Include("DeviceValues.Option").toList().GroupJoin
.Include() does not enforce evaluation, it only minimizes the number of calls to your database.

How to debug query in c# program?

I want to write query more efficient.
I do not want before the end of the query, the list of data to extract.
var UserTimeLineNews = (from l in _newsService.NewsQuery()
where l.UserId == UserId && l.IsActive == true
orderby l.CreateDate descending
select new UserTimeLine
{
EventDate = l.CreateDate,
CreateDate = l.CreateDate,
NewsId = l.NewsId,
TimeLineType = TimeLineType.CreateNews,
Title = l.Title,
Abstract = l.NewsAbstract,
CommentCount = l.CommentCount,
LikeCount = l.LikeCount,
ViewsCount = l.ViewsCount,
Storyteller = l.Storyteller
}).AsQueryable();//Take(NumberOfNews).ToList();
var UserTimeLineLikeNews = (from l in _likeNewsService.LikeNewsQueryable()
where l.UserId == UserId
orderby l.CreateDate descending
select new UserTimeLine
{
EventDate = l.CreateDate,
CreateDate = l.CreateDate,
NewsId = l.NewsId,
TimeLineType = TimeLineType.LikeNews,
Title = l.News.Title,
Abstract = l.News.NewsAbstract,
CommentCount = l.News.CommentCount,
LikeCount = l.News.LikeCount,
ViewsCount = l.News.ViewsCount,
Storyteller = l.News.Storyteller
}).AsQueryable();//Take(NumberOfNews).ToList();
var UserTimeLineComments = (from l in _commentService.CommentQueryable()
where l.UserId == UserId && l.IsActive == true
orderby l.CreateDate descending
select new UserTimeLine
{
EventDate = l.CreateDate,
CreateDate = l.CreateDate,
NewsId = l.NewsId,
TimeLineType = TimeLineType.Comment,
Title = l.News.Title,
Abstract = l.News.NewsAbstract,
CommentContent = l.Content,
CommentCount = l.News.CommentCount,
LikeCount = l.News.LikeCount,
ViewsCount = l.News.ViewsCount,
Storyteller = l.News.Storyteller
}).AsQueryable();//Take(NumberOfNews).ToList();
var item = (UserTimeLineNews
.Union(UserTimeLineLikeNews)
.Union(UserTimeLineComments))
.OrderByDescending(e => e.EventDate)
.Distinct()
.Take(NumberOfNews)
.ToList();
After running the following error appears
Error:
The type 'UserTimeLine' appears in two structurally incompatible initializations within a single LINQ to Entities query.
A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.
The first two queries don't initialize the CommentContent property. Add that to the initializer in the first two queries (or remove it in the last query) and the final query should work.

The entity or complex type cannot be constructed in a LINQ to Entities query

On our online billing application, we give a billing summary of what bills the customer received and the payments they made.
In order for this to work, I have to first pull the payments then match them to the bills. So I have do something like:
foreach (BillPaymentSummary payment in billPayments)
{
DateTime dt = payment.DueDate;
// Debug errors on this next line
var summary = (from a in db.BillHistories
where a.CustomerId == customerNumber && a.DueDate == dt && a.Type == "BILL"
select new BillSummary
{
Id = a.Id,
CustomerId = a.CustomerId,
DueDate = a.DueDate,
PreviousBalance = a.PreviousBalance.Value,
TotalBill = a.TotalBill.Value,
Type = a.Type,
IsFinalBill = a.IsFinalBill
}).SingleOrDefault();
if (summary != null)
{
summary.PayDate = payment.PaidDate;
summary.AmountPaid = payment.AmountPaid;
returnSummaries.Add(summary);
}
else
{
summary = (from a in db.BillHistories
where a.CustomerId == customerNumber && a.DueDate == payment.DueDate && a.Type == "ADJ "
select new BillSummary
{
Id = a.Id,
CustomerId = a.CustomerId,
DueDate = a.DueDate,
PreviousBalance = a.PreviousBalance.Value,
TotalBill = a.TotalBill.Value,
Type = a.Type,
IsFinalBill = a.IsFinalBill
}).SingleOrDefault();
if (summary != null)
{
summary.PayDate = payment.PaidDate;
summary.AmountPaid = payment.AmountPaid;
returnSummaries.Add(summary);
}
}
}
I have been playing with this, but no matter what I do, I get the following error message:
The entity or complex type 'UtilityBill.Domain.Concrete.BillSummary' cannot be constructed in a LINQ to Entities query.
Is it because I am running queries within queries? How can I get around this error?
I have tried searching Google for an answer and see many answers, but none of them seem to explain my problem.
You cannot project onto a mapped entity. You would have to call ToList() before doing your mapping.
Or better yet, change to the following (calling FirstOrDefault will execute the query and allow you to populate your object):
var summary = db.BillHistories.FirstOrDefault(a => a.CustomerId == customerNumber && a.DueDate == dt && a.Type == "BILL").Select(x => new BillSummary
{
Id = a.Id,
CustomerId = a.CustomerId,
DueDate = a.DueDate,
PreviousBalance = a.PreviousBalance.Value,
TotalBill = a.TotalBill.Value,
Type = a.Type,
IsFinalBill = a.IsFinalBill
});
To decouple yourself from the Entity Framework you may want to also consider using a different model class to return instead of the Entity Framework model.
What I ended up doing was:
foreach (BillPaymentSummary payment in billPayments)
{
var data = db.BillHistories.Where(b => b.CustomerId == customerNumber && b.DueDate == payment.DueDate && b.Type == "B").FirstOrDefault();
if (data != null) // There is a bill history
{
returnSummaries.Add(new BillSummary
{
Id = data.Id,
CustomerId = data.CustomerId,
DueDate = data.DueDate,
PreviousBalance = data.PreviousBalance,
TotalBill = data.TotalBill,
Type = (data.Type.Trim() == "B" ? "BILL" : (data.Type == "A" ? "ADJ" : "")),
IsFinalBill = data.IsFinalBill,
PayDate = payment.PaidDate,
AmountPaid = payment.AmountPaid
});
}
else // No bill history record, look for an adjustment
{
data = db.BillHistories.FirstOrDefault(b => b.CustomerId == customerNumber && b.DueDate == payment.DueDate && b.Type == "A");
if (data != null)
{
returnSummaries.Add(new BillSummary
{
Id = data.Id,
CustomerId = data.CustomerId,
DueDate = data.DueDate,
PreviousBalance = data.PreviousBalance,
TotalBill = data.TotalBill,
Type = (data.Type.Trim() == "B" ? "BILL" : (data.Type == "A" ? "ADJ" : "")),
IsFinalBill = data.IsFinalBill,
PayDate = payment.PaidDate,
AmountPaid = payment.AmountPaid
});
}
}
db.SaveChanges();
}

Categories