I had a problem with the following code. When I don't call ToList() on the initial RavenSession.Query<Item>() call, the PhotoPath property is null in the ItemSummaryModel object. Is this a lazy loading issue or something else that's causing this?
The PhotoPath property was null on the initial save of this document. I then updated it in a subsequent edit.
When I query for the full item instead of selecting a new object it works as expected populating all properties.
Why did I have to force query execution with ToList() for the new ItemSummaryModel to be populated as expected?
var fullItems = RavenSession.Query<Item>().ToList();
var items = (from i in fullItems
where i.DateAdded >= DateTime.Now.Subtract(new TimeSpan(10,0,0,0))
orderby i.DateAdded
select new ItemSummaryModel()
{
Id = i.Id,
PhotoPath = i.ListingPhotoPath,
MarketingInfo = i.MarketingInfoShort,
Name = i.Name,
Summary = i.Summary,
PriceTypeCode = i.ClearancePrice > 0 ? PriceType.Clearance : (i.SalePrice > 0 ? PriceType.Sale : PriceType.List),
ListSaleOrClearancePrice = i.ClearancePrice > 0 ? i.ClearancePrice : (i.SalePrice > 0 ? i.SalePrice : i.Price)
}).Take(nbrOfItems);
return items;
RavenDB's linq provider is pretty simplistic, it can't currently handle field remapping.
In other words, it can't handle it that you did this:
PhotoPath = i.ListingPhotoPath,
If you changed it to
ListingPhotoPath = i.ListingPhotoPath,
It will work.
That is an issue that is scheduled to be fixed
Related
I have a complex query that seems to cause a memory leak in my applications memory.
From my understanding the query result is cached so that this processing does not need to be done every time the query is executed. https://learn.microsoft.com/en-us/ef/core/querying/how-query-works
But what it looks like is that the query is getting cached for each and every user that logs into the system. From the memory dump it looks like we have thousands of compiled query cache objects for the same query.
The query looks as follows.
public async Task<IList<EmployeeInboxMessage>> GetEmployeeMessagesAsync(long employeeId)
{
return await (from message in this.Repository.Set
join userStep in this.Repository.Context.Set<UserWorkflowHeaderStep>() on message.UserWorkflowStepId equals userStep.UserWorkflowStepId into userSteps
from userStep in userSteps.DefaultIfEmpty()
join acceptedStep in this.Repository.Context.Set<CompanyWorkflowStep>() on userStep.AcceptedStepId equals acceptedStep.WorkflowStepId into acceptedSteps
from acceptedStep in acceptedSteps.DefaultIfEmpty()
join rejectedStep in this.Repository.Context.Set<CompanyWorkflowStep>() on userStep.RejectedStepId equals rejectedStep.WorkflowStepId into rejectedSteps
from rejectedStep in rejectedSteps.DefaultIfEmpty()
let step =
message.InboxEntryType == InboxEntryType.Claims ||
message.InboxEntryType == InboxEntryType.AdvancedLeave ||
message.InboxEntryType == InboxEntryType.ChangeRequest
? new WorkflowHeaderStep
{
WorkflowItem = userStep.WorkflowItem,
AcceptedStepId = userStep.AcceptedStepId,
AcceptedStep = acceptedStep == null ? null : new WorkflowStep
{
OnApprovalActionId = acceptedStep.OnApprovalActionId,
OnRejectionAction = acceptedStep.OnRejectionAction,
OrderNumber = acceptedStep.OrderNumber
},
RejectedStepId = userStep.RejectedStepId,
RejectedStep = rejectedStep == null ? null : new WorkflowStep
{
OnApprovalActionId = rejectedStep.OnApprovalActionId,
OnRejectionAction = rejectedStep.OnRejectionAction,
OrderNumber = rejectedStep.OrderNumber
}
}
: null
let employeeName = string.IsNullOrWhiteSpace(message.OBOEmployee.PreferredName)
? message.OBOEmployee.FullName
: message.OBOEmployee.PreferredName + " " + message.OBOEmployee.LastName
where message.EmployeeId == employeeId
orderby message.EffectiveDate
select new EmployeeInboxMessage
{
Message = message,
UserStep = step,
UserId = message.UserId,
UserName = message.User.FullName,
EmployeeName = message.OBOEmployeeId.HasValue
? employeeName
: message.User.FullName,
RelatedPrimaryKey =
message.InboxEntryType == InboxEntryType.Claims ||
message.InboxEntryType == InboxEntryType.AdvancedLeave ||
message.InboxEntryType == InboxEntryType.ChangeRequest
? userStep.RelatedPrimaryKey
: message.UserWorkflowStepId!.Value,
StartUserCompanyId = message.StartUser.CompanyId
}).ToListAsync();
}
It looks like your query is to complex for EF to create a single CompiledQueryCache object and it is adding a cached version for each "employeeId" that you are passing in. By the looks of it there is already 27804 versions.
If you restructure your query then it might solve your problem. Try removing the "let step = ..." and "let employeeName = ...". Worst case you would need to create raw SQL or maybe a view.
There is not a lot of documentation out there explaining how EF creates the query cache(I can't find any). You might never now what exactly your problem is except if some of the EF creators give some input or you could always go through their code.
If you still have a problem after changing your query it might be best to log a issue with efcore https://github.com/dotnet/efcore
in my query when i see quick watch, under the result it returns no result but when go through Non-Public members under source and result in view I see all my results,how can i access to them? and why its like this?im using PostgreSQL for my database
var test = (from t in db.v_vpn_gateway.AsEnumerable()
where t.turbine_id.ToString() == id
select new TurbineDvce
{
Comments = "VPN Gateway",
Description = string.Empty,
DeviceIP = t.vpn_gateway.ToString(),
DeviceType = t.device_type,
FirmwareVersion = string.Empty,
Model = t.model,
Password = string.Empty,
Phone = string.Empty,
Producer = t.producer,
PublicIP = t.vpn_public_ip.ToString(),
TurbineId = t.turbine_id.ToString(),
Username = string.Empty
});
Looks like you need to "Hydrate" the result. The way Linq works with Lazy Evaluation, really you are just getting a place holder back for the test variable, and the actual query will run when the test variable is used by other code. Since you have a "where" clause, you should be expecting back an IEnumerable, so you can add ToList() or ToArray() to the end of your query, which will force the query to run and store the list or array in your test variable. That should give you access to that data right after the query runs.
i am using viewmodel to display data from two tables (Eta and Voyage) and i have used viewmodel name as 'EtaVoyage'.The problem is when i use this query, it gives me this error
Additional information: Object reference not set to an instance of an object.
var Test = db.Etas.AsEnumerable().Select(v => new EtaVoyage()
{
ShippingAgent = v.ShippingAgent,
VesselInformation = v.VesselInformation,
Port = v.Port,
CPort = v.CPort,
EtaDate = v.EtaDate,
GoodsCarried = v.VoyageDetails.FirstOrDefault().GoodsCarried,
VoyagePurpose = v.VoyageDetails.FirstOrDefault().VoyagePurpose
}).ToList();
return View(Test);
But when i comment the last two fields related to voyagedetails, it is working fine.
var Test = db.Etas.AsEnumerable().Select(v => new EtaVoyage()
{
ShippingAgent = v.ShippingAgent,
VesselInformation = v.VesselInformation,
Port = v.Port,
CustomPort = v.CustomPort,
EtaDate = v.EtaDate,
// GoodsCarried = v.VoyageDetails.FirstOrDefault().GoodsCarried,
// VoyagePurpose = v.VoyageDetails.FirstOrDefault().VoyagePurpose
}).ToList();
return View(Test);
i need to display these two columns too in the index page.
FirstOrDefault() might return null,
Enumerable.FirstOrDefault : Return Value
Type: TSource
default(TSource) if source is empty; otherwise, the first element in source.
Use
.Select(i=>i.GoodsCarried).FirstOrDefault()
....
GoodsCarried = v.VoyageDetails.Select(i=>i.GoodsCarried).FirstOrDefault(),
VoyagePurpose = v.VoyageDetails.Select(i=>i.VoyagePurpose).FirstOrDefault()
}).ToList();
The collection v.VoyageDetails must not contain any items, and therefore FirstOrDefault is returning the default (null for reference types). You can handle this special case separately, or, since you seem to just be flattening a collection, you can use a null-conditional operator to set GoodsCarried and VoyagePurpose to null when FirstOrDefault returns null.
GoodsCarried = v.VoyageDetails.FirstOrDefault()?.GoodsCarried,
VoyagePurpose = v.VoyageDetails.FirstOrDefault()?.VoyagePurpose
Note it is also possible that:
v.VoyageDetails itself is null, depending on how your class is initialized and data is loaded. If this is expected, you may need to handle this case as well. Again with the null-conditional operator:
GoodsCarried = v.VoyageDetails?.FirstOrDefault()?.GoodsCarried,
VoyagePurpose = v.VoyageDetails?.FirstOrDefault()?.VoyagePurpose
If you are using an ORM such as Entity Framework, the VoyageDetails collection is not eagerly loaded, it may simply not be retrieving the data for you. If this applies, you need to explicitly load the data in the collection. In Entity Framework this is done with an Include call. Note your AsEnumerable() call will stop Linq-To-Sql from optimizing this into a single query, but I assume this is intentional:
db.Etas.Include(x => x.VoyageDetails).AsEnumerable().Select(...)
My code is as below:
var conntionRecord1Id = (from connectionBase in orgServiceContext.CreateQuery("connection")
where connectionBase["record1roleid"] == null
select new { OpportunityId = connectionBase["record1id"] }).Distinct().ToList();
var query =
from opportunity in orgServiceContext.CreateQuery("opportunity")
orderby opportunity["createdon"] ascending
select new
{
Topic = opportunity.Attributes.Contains("name") == true ? opportunity["name"] : null,
OpportunityId = opportunity.Attributes.Contains("opportunityid") == true ? opportunity["opportunityid"] : null,
PostalCode = opportunity.Attributes.Contains("new_address_postalcode") == true ? opportunity["new_address_postalcode"] : null,
};
var result = (from f in query.ToList() where conntionRecord1Id.Contains(f.OpportunityId) select f).ToList();
But in above query where i am using Contains it giving count 0. even though I have common records in the list
The Contains Linq extension method does not know how to compare complex objects.
Comparing f.OpportunityId to a list of Guids instead of a list of an anonymous type will work:
var conntionRecord1Id = (from connectionBase in orgServiceContext
.CreateQuery("connection")
where connectionBase["record1roleid"] == null
select connectionBase.GetAttributeValue<Guid?>("record1id"))
.Distinct()
.ToList();
Implement IEqualityComparer in a custom comparer class to compare complex objects: https://stackoverflow.com/a/6694563/1817350
Keep in mind that calling .ToList() brings down all the records into memory and will cause performance problems with large amounts of records.
I have the following code
nodes = data.Descendants(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Results")).Nodes();
System.Collections.Generic.IEnumerable<Result> res = new List<Result>();
if (nodes.Count() > 0)
{
var results = from uris in nodes
select new Result
{
URL =
((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Url")).Value,
Title =
((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Title")).Value,
Description =
((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Description")).Value,
DateTime =
((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}DateTime")).Value,
};
res = results;
}
Where Results is a object who has those URL, Title, Description, and DateTime variables defined.
This all works fine normally, but when a 'node' in nodes doesnt contain a Description element (or at least I think thats whats throwing it) the program hits the "res = results;"
line of code and throws a 'object reference not set to...' error and highlights the whole section right after "select new Results"..
How do I fix this?
The simplest way is to cast to string instead of using the Value property. That way you'll end up with a null reference for the Description instead.
However, your code can also be made a lot nicer:
XNamespace ns = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/web";
var results = data.Descendants(ns + "Results")
.Elements()
.Select(x => new Result
{
URL = (string) x.Element(ns + "Url"),
Title = (string) x.Element(ns + "Title"),
Description = (string) x.Element(ns + "Description"),
DateTime = (string) x.Element(ns + "DateTime")
})
.ToList();
See how much simpler that is? Techiques used:
Calling ToList() on an empty sequence gives you a list anyway
This way you'll only ever perform the query once; before you were calling Count() which would potentially have iterated over each node. In general, use Any() instead of Count() > 0) - but this time just making the list unconditional is simpler.
Use the Elements() method to get child elements, rather than casting multiple times. (Your previous code would have thrown an exception if it had encountered any non-element nodes)
Use the implicit conversion from string to XNamespace
Use the +(XNamespace, string) operator to get an XName
If the Description element is not included you should test if this
((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Description"))
is not null before using Value. Try this code:
var results = from uris in nodes let des = ((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Description"))
select new Result
{
URL = ((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Url")).Value,
Title = ((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Title")).Value,
Description = (des != null) ? des.Value : string.Empty,
DateTime = ((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}DateTime")).Value,
};