I have defined the following entity classes for my LINQ query:
public class Application
{
public Application() { }
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public System.DateTime DateTimeCreated { get; set; }
public System.DateTime? DateTimeModified { get; set; }
public Employee CreatedBy { get; set; }
public Employee ModifiedBy { get; set; }
}
public class Employee
{
public Employee() { }
public string EmployeeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
I have created the following query to create an Application object and trying to create an Employee entity for the CreatedBy and 'ModifiedBy' properties. Sometimes, the ModifiedBy column can contain null and I want to set the ModifiedBy property to null.
var query = from a in context.Applications
join u1 in context.Employees on a.CreatedBy equals u1.Employee_ID.Trim()
join u2 in context.Employees on a.ModifiedBy equals u2.Employee_ID.Trim()
where a.ApplicationId == applicationId
select new Entity.Application
{
Id = a.ApplicationId,
Name = a.ApplicationName,
Description = a.ApplicationDesc,
DateTimeCreated = a.DateTimeCreated,
CreatedBy = new Entity.Employee{ EmployeeID = a.CreatedBy, FirstName = u1.First_Name, LastName = u1.Last_Name },
DateTimeModified = a.DateTimeModified ?? null,
ModifiedBy = (a.ModifiedBy != null) ? new Entity.Employee { EmployeeID = a.ModifiedBy, FirstName = u2.First_Name, LastName = u2.Last_Name } : (Entity.Employee) null,
};
When I debug the query above, I get the following error:
Type of conditional expression cannot be determined because there is
no implicit conversion between 'Employee' and 'Application'
How do I resolve this error?
It may not directly address an error you're getting, but you don't need that query at all, because you have navigation properties set. Use Include instead and it should work just fine - EF will join necessary joins for you:
var query context.Applications
.Include("ModifiedBy")
.Include("CreatedBy")
.Where(a => a.ApplicationId == applicationId);
A couple things I am noticing
join u1 in context.Employees on a.CreatedBy equals u1.Employee_ID.Trim()
join u2 in context.Employees on a.ModifiedBy equals u2.Employee_ID.Trim()
You can't join like this, as CreateBy and ModifiedBy are of type Employee, not string
Also, have a look at this:
(Entity.Employee) null
You can't cast null to Employee. You might want to use the type's default value in future:
default(Entity.Employee)
UPDATE
As pointed out in the comments, it is legal to cast null to Entity.Employee, but since you end up with null anyways there is not much point to the exercise. default(Entity.Employee) also results in null, since that's the default value for reference types, but default could provide a different value for various other types, which can sometimes be useful.
After some additional research, here is the revised code snippet that ended up working for me:
var result = (from a in context.Applications
join u1 in context.Employee on a.CreatedBy equals u1.Employee_ID.Trim()
join u2 in context.Employee on a.ModifiedBy equals u2.Employee_ID.Trim() into us
from u2 in us.DefaultIfEmpty()
where a.ApplicationId == applicationId
select new Entity.Application()
{
Id = a.ApplicationId,
Name = a.ApplicationName,
Description = a.ApplicationDesc,
DateTimeCreated = a.DateTimeCreated,
CreatedBy = new Entity.Employee
{
EmployeeID = a.CreatedBy,
FirstName = u1.First_Name,
LastName = u1.Last_Name
},
DateTimeModified = a.DateTimeModified ?? null,
ModifiedBy = new Entity.Employee
{
EmployeeID = a.ModifiedBy ?? string.Empty,
FirstName = u2.First_Name ?? string.Empty,
LastName = u2.Last_Name ?? string.Empty
}
}).Single();
Related
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.
This is just strange behavior to me.
LINQ transforming my statements creating bizarre execution plans. It's adding sub-queries when I don't ask it to and it's including or excluding joins in this sub-query based on the order of my joins, which leaves me scratching my head. At this point it's impossible for me to optimize this script based on the unpredictable nature of LINQ to Entities.
SETUP
//define global filter
Expression<Func<Contract_SeqExecution, bool>> globalFilter = r => r.ModifiedByFirstName.Contains("w");
...
//extend global filter
Expression<Func<Contract_SeqExecution, bool>> descendantFilter = x => x.client_id == 1 && x.project_status == true && x.parentId != null;
descendantFilter = descendantFilter.And(globalFilter);
//query
IQueryable<Contract_SeqExecution> geDescendantResults = c.queryGroups(this);
//execute
geDescendantResults.AsExpandable().Where(descendantFilter).Dump();
LINQ QUERY
public IQueryable<Contract_SeqExecution> queryGroups(UserQuery context)
{
var result = (from ge in context.group_execution
join aseq in context.automation_sequences on ge.automation_sequence_id equals aseq.id
join modify_u in context.users on aseq.last_modified_by_id equals modify_u.id into modify_uSub
from modify_u in modify_uSub.DefaultIfEmpty()
join p in context.project on aseq.project_id equals p.id
join asstatus in context.automation_sequence_status on ge.run_status_id equals asstatus.id
join es in context.execution_schedule on ge.schedule_id equals es.id
join exe_u in context.users on ge.executed_by_id equals exe_u.id
join create_u in context.users on aseq.created_by_id equals create_u.id
select new Contract_SeqExecution
{
client_id = p.client_id,
project_status = p.status,
ID = ge.id,
Name = aseq.name,
ModifiedByFirstName = modify_u.first_name,
ModifiedByLastName = modify_u.last_name,
FailedInd = asstatus.fail_alert_ind,
parentId = ge.parent_group_exec_id,
patriarchId = ge.patriarch_id
});
return result;
}
If I reorder the joins in the above statement my execution plan changes and LINQ-to-entities decides I want a sub-query....sigh.
Seriously all I did was change the order of the joins in the images below. Drastically different execution plans which will later on hurt performance of the query. What gives? Am I missing something obvious?
Is it picking information up from the entity framework schema? Could it be missing FK definitions or indexes that are driving this crazy behavior? At this point I'm just shooting in the dark. Hopefully someone here can shed some light on this.
Below is the class Contract_SeqExecution. I'm currently running this in a LinqPad C# Program hitting my DAL dll, linq-to-entities.
public class Contract_SeqExecution
{
public int ID { get; set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public int SeqID { get; set; }
public int CaseGroupInd { get; set; }
public string CaseGroupText { get; set; }
public string ExecRatio { get; set; }
public string ModifiedByFirstName { get; set; }
public string ModifiedByLastName { get; set; }
public string Project { get; set; }
public string Uploaded { get; set; }
public string UploadRatio { get; set; }
public bool FailedInd { get; set; }
public bool HoldInd { get; set; }
public int? parentId { get; set; }
public int? patriarchId { get; set; }
public DateTime? SchedRunTime { get; set; }
//other fields
public string Machine {get;set;}
public int is_accepting_changes {get;set;}
public string holding_at_name {get;set;}
public string RunStatus {get;set;}
public string CaseGroupStatus {get; set;}
public string TCStatusColor {get; set;}
public string ExecutedBy {get;set;}
public string CreatedBy {get;set;}
public int? ScheduleID {get;set;}
public bool SeqUploaded {get;set;}
//end other fields
public int? parent_group_exec_id { get; set; }
public int client_id { get; set; }
public bool project_status { get; set; }
public IQueryable<Contract_SeqExecution> queryGroups(UserQuery context)
{
var result = (from ge in context.group_execution
join asstatus in context.automation_sequence_status on ge.run_status_id equals asstatus.id
join es in context.execution_schedule on ge.schedule_id equals es.id
join exe_u in context.users on ge.executed_by_id equals exe_u.id
join aseq in context.automation_sequences on ge.automation_sequence_id equals aseq.id
join p in context.project on aseq.project_id equals p.id
join create_u in context.users on aseq.created_by_id equals create_u.id
join modify_u in context.users on aseq.last_modified_by_id equals modify_u.id into modify_uSub
from modify_u in modify_uSub.DefaultIfEmpty()
select new Contract_SeqExecution
{
client_id = p.client_id,
project_status = p.status,
ID = ge.id,
Name = aseq.name,
ModifiedByFirstName = modify_u.first_name,
ModifiedByLastName = modify_u.last_name,
FailedInd = asstatus.fail_alert_ind,
parentId = ge.parent_group_exec_id,
patriarchId = ge.patriarch_id,
});
return result;
}
}
UPDATE
So I took Andrew's advice and looked into doing this the more "LINQ" way. I like to include FKs in my entities because I feel it's a more natural way to write SQL, habits die hard. I went ahead and rewrote my LINQ expression and the results are now consistent.
Example:
var result = (from ge in context.group_execution
//join asstatus in context.automation_sequence_status on ge.run_status_id equals asstatus.id
//join es in context.execution_schedule on ge.schedule_id equals es.id
//join exe_u in context.users on ge.executed_by_id equals exe_u.id
//join aseq in context.automation_sequences on ge.automation_sequence_id equals aseq.id
//join p in context.project on aseq.project_id equals p.id
//join create_u in context.users on aseq.created_by_id equals create_u.id
//join modify_u in context.users on aseq.last_modified_by_id equals modify_u.id into modify_uSub
//from modify_u in modify_uSub.DefaultIfEmpty()
select new Contract_SeqExecution
{
//client_id = p.client_id,
//project_status = p.status,
ID = ge.id,
Name = ge.automation_sequences.name,
ModifiedByFirstName = ge.automation_sequences.modified_by_user.first_name,
ModifiedByLastName = ge.automation_sequences.modified_by_user.last_name,
//FailedInd = asstatus.fail_alert_ind,
parentId = ge.parent_group_exec_id,
patriarchId = ge.patriarch_id,
});
return result;
I have a problem with my virtual link. Entity framework doesn't seem to eager load in cases where the UserID = 0. I'm using Entity Framework 6 .NET Framework 4.5.2 and VS2013
public class Booking
{
[Key]
public int Booking_ID { get; set; }
public int Customer_ID { get; set; }
public DateTime StartDate_DT { get; set; }
public DateTime BookingDate_DT { get; set; }
public int UserID { get; set; }
public virtual UserProfile UserProfile { get; set; }
}
This does not include any record from the Booking table with a UserID of 0:
var list = (from booking in _db.Booking
where booking.Customer_ID == customerId
orderby booking.StartDate_DT, booking.BookingDate_DT
select new CustomerBookingLine
{
BookingId = b.Booking_ID,
StartDate = b.StartDate_DT,
BookingDate = b.BookingDate_DT,
UserName = b.UserProfile != null ? b.UserProfile.UserName : null
}).ToList();
return list;
This does return the booking records including eager-loaded user profile records
var list = (from b in _db.Booking
where b.Customer_ID == customerId
orderby booking.StartDate_DT, booking.BookingDate_DT
select b).ToList();
return (from b in bookingList
select new CustomerBookingLine
{
BookingId = b.Booking_ID,
StartDate = b.StartDate_DT,
BookingDate = b.BookingDate_DT,
UserName = b.UserProfile != null ? b.UserProfile.UserName : null
}).ToList();
I've tried using includes but the 2nd method is the only way I can get it to work. Can anyone explain why?
This question already has answers here:
How to use ToShortDateString in linq lambda expression?
(4 answers)
Closed 6 years ago.
I am getting the following error: "Could not translate expression..." when I try to return a list of enquiry entities, converted to a custom class.
I am very new to using link in this way, in the past I would use stored procedure in SQL and just import them as methods but I am trying to convert these.
My method that returns the list is:
public static List<EnquiryData> GetAllEnquiries()
{
var GridData = from a in Global.AcepakSalesPortal.Enquiries
join Cust in Global.AcepakSalesPortal.Customers
on a.CustomerID equals Cust.CustomerID into CustGroup
from b in CustGroup.DefaultIfEmpty()
join Pros in Global.AcepakSalesPortal.Prospects
on a.ProspectID equals Pros.ProspectID into ProsGroup
from c in ProsGroup.DefaultIfEmpty()
join Users in Global.AcepakSalesPortal.Users
on a.ResponsiblePartyID equals Users.UserID into UserGroup
from d in UserGroup.DefaultIfEmpty()
join Qt in Global.AcepakSalesPortal.Quotes
on a.QuoteID equals Qt.QuoteID into QuoteGroup
from e in QuoteGroup.DefaultIfEmpty()
join Usr in Global.AcepakSalesPortal.Users
on e.CreatedBy equals Usr.UserID into UsrGroup
from f in UsrGroup.DefaultIfEmpty()
join EnqCat in Global.AcepakSalesPortal.EnquiryCategories
on a.EnquiryCategoriesID equals EnqCat.EnquiryCatID into CatGroup
from g in CatGroup.DefaultIfEmpty()
join Clsd in Global.AcepakSalesPortal.Users
on a.ClosedBy equals Clsd.UserID into ClsdGroup
from h in ClsdGroup.DefaultIfEmpty()
orderby a.Created descending
select new EnquiryData
{
EnquiryID = a.EnquiryID,
ResponsiblePartyID = a.ResponsiblePartyID,
EnquiryNo = "ENQ" + a.EnquiryID.ToString().PadLeft(7, '0'),
EType = a.CustomerID.HasValue ? "C" : "P",
EnqCat = g.Code + " - " + g.Category,
ContactPerson = a.ProspectID.HasValue ? c.ContactPerson : "NOT INTEGRATED YET",
ContactNumber = a.ProspectID.HasValue ? c.ContactNum : "NOT INTEGRATED YET",
ContactEmail = a.ProspectID.HasValue ? c.ContactEmail : "NOT INTEGRATED YET",
Company = a.CustomerID.HasValue ? b.Name : c.CompanyName,
Description = a.Description,
AssignedTo = d.Name,
AddressBy = a.AddressBy,
EnquiryDate = a.Created,
EStatus = a.Closed.HasValue ? "Closed" : a.QuoteID.HasValue ? "Quoted" : "Open",
QuotedOn = a.QuoteID.HasValue ? e.Created.ToShortDateString() : "N/A",
QuotedBy = a.QuoteID.HasValue ? f.Name : "N/A",
QuoteNum = a.QuoteID.HasValue ? e.QuoteID.ToString().PadLeft(7, '0') : "N/A",
ClosedOn = a.Closed.HasValue ? a.Closed.Value.ToShortDateString() : "N/A",
ClosedBy = a.Closed.HasValue ? h.Name : "N/A",
Reason = a.Closed.HasValue ? a.ClosedReason : "N/A"
};
return GridData.ToList();
}
And the custom class is:
public class EnquiryData
{
public int EnquiryID { get; set; }
public int ResponsiblePartyID { get; set; }
public string EnquiryNo { get; set; }
public string EType { get; set; }
public string EnqCat { get; set; }
public string ContactPerson { get; set; }
public string ContactNumber { get; set; }
public string ContactEmail { get; set; }
public string Company { get; set; }
public string Description { get; set; }
public string AssignedTo { get; set; }
public DateTime AddressBy { get; set; }
public DateTime EnquiryDate { get; set; }
public string EStatus { get; set; }
public string QuotedOn { get; set; }
public string QuotedBy { get; set; }
public string QuoteNum { get; set; }
public string ClosedOn { get; set; }
public string ClosedBy { get; set; }
public string Reason { get; set; }
}
My question is 2 fold
1. Is there a better ways to join tables together in Linq than I am doing above?
2. What could be causing the error, I dont mind figuring it out but not sure how to even approach this.
EDIT: This is most definitely not a duplicate of the mentioned question. The only similarity between the 2 is the use of shortdatestring, however the error message I receive is completely different to that of the other question.
You have used lot of c# methods which are not known by the SQL.Hence you can retrieve all the columns as shown below and then do your custom mapping on the memory as you wish.
var GridData = (from a in Global.AcepakSalesPortal.Enquiries
join Cust in Global.AcepakSalesPortal.Customers
on a.CustomerID equals Cust.CustomerID into CustGroup
from b in CustGroup.DefaultIfEmpty()
join Pros in Global.AcepakSalesPortal.Prospects
on a.ProspectID equals Pros.ProspectID into ProsGroup
from c in ProsGroup.DefaultIfEmpty()
join Users in Global.AcepakSalesPortal.Users
on a.ResponsiblePartyID equals Users.UserID into UserGroup
from d in UserGroup.DefaultIfEmpty()
join Qt in Global.AcepakSalesPortal.Quotes
on a.QuoteID equals Qt.QuoteID into QuoteGroup
from e in QuoteGroup.DefaultIfEmpty()
join Usr in Global.AcepakSalesPortal.Users
on e.CreatedBy equals Usr.UserID into UsrGroup
from f in UsrGroup.DefaultIfEmpty()
join EnqCat in Global.AcepakSalesPortal.EnquiryCategories
on a.EnquiryCategoriesID equals EnqCat.EnquiryCatID into CatGroup
from g in CatGroup.DefaultIfEmpty()
join Clsd in Global.AcepakSalesPortal.Users
on a.ClosedBy equals Clsd.UserID into ClsdGroup
from h in ClsdGroup.DefaultIfEmpty()
orderby a.Created descending
select a).ToList()
After that do your custom class mapping here :
var list= GridData.Select(a=>new EnquiryData{EnquiryID = a.EnquiryID,.... })
1.When these query executes, linq complier needs to transform this linq queries to SQL query.Most of the methods implementing IQuerable inferface, can be convertable. but C# methods like ToString() , ToShortDateString() could not be able to convert by Linq complier. so you get 'Could not translate expression..'.
You could try:
var GridData = from a in Global.AcepakSalesPortal.Enquiries
join Cust in Global.AcepakSalesPortal.Customers on a.CustomerID equals Cust.CustomerID
join Pros in Global.AcepakSalesPortal.Prospects on a.ProspectID equals Pros.ProspectID
join Users in Global.AcepakSalesPortal.Users on a.ResponsiblePartyID equals Users.UserID
etc..
I'm having real trouble with a join that I am trying to perform on two separate XML files.
I have two classes that describe each object that I extract from the XML files, they are
public class XMLCustomers
{
public String CustomerID { get; set; }
public String ContactName { get; set; }
}
and
public class XMLOrders
{
public String OrderID { get; set; }
public String CustomerID { get; set; }
public String OrderDate { get; set; }
public String ShippedDate { get; set; }
public String Freight { get; set; }
public String ShipName { get; set; }
public String ShipCity { get; set; }
public String ShipCountry { get; set; }
}
And my last class stores the join that I perform on the two sets of data.
public class PostXMLJoinOrder
{
public String OrderID { get; set;}
public String ContactName { get; set;}
public String OrderDate { get; set;}
public String ShippedDate { get; set;}
public String Freight { get; set;}
public String ShipName { get; set;}
public String ShipCity { get; set;}
public String ShipCountry { get; set;}
}
Finally these are my two methods that load the information from the XML files and the third method performs a join and stores the information in a IEnumerable
public IEnumerable<XMLCustomers> LoadXMLCustomers() {
var custs = from x in XElement.Load(System.Web.HttpContext.Current.Server.MapPath(#"~\App_Data\XCustomers.xml")).Elements()
select new XMLCustomers
{
ContactName = x.Attribute("ContactName").Value,
CustomerID = x.Attribute("CustomerID").Value
};
int size = custs.Count();
return custs;
}
public IEnumerable<XMLOrders> LoadXMLOrders() {
var ords = from x in XElement.Load(System.Web.HttpContext.Current.Server.MapPath(#"~\App_Data\XOrders.xml")).Elements()
select new XMLOrders
{
OrderID = x.Attribute("ContactName").Value,
CustomerID = x.Attribute("CustomerID").Value,
OrderDate = x.Attribute("OrderDate").Value,
ShippedDate = x.Attribute("ShippedDate").Value,
Freight = x.Attribute("Freight").Value,
ShipName = x.Attribute("ShipName").Value,
ShipCity = x.Attribute("ShipCity").Value,
ShipCountry = x.Attribute("ShipCountry").Value
};
int size = ords.Count();
return ords;
}
public IEnumerable<PostXMLJoinOrder> LoadPostXMLJoinOrders() {
var joinQuery = from customer in LoadXMLCustomers()
from orders in LoadXMLOrders()
where customer.CustomerID == orders.CustomerID
select new PostXMLJoinOrder
{
OrderID = orders.OrderID,
ContactName = customer.ContactName,
OrderDate = orders.OrderDate,
ShippedDate = orders.ShippedDate,
Freight = orders.Freight,
ShipName = orders.ShipName,
ShipCity = orders.ShipCity,
ShipCountry = orders.ShipCountry
};
return joinQuery;
}
I have tested the amount of items returned from my LINQ and it continues to be 0;
I have double checked everything being loaded in from the files but I cant seem to get my head around at which point its going wrong.
Edit:
its a loading issue. the XML files are definitely stored under the correct App_Data folder. But when the individial LoadXMLCustomers() is running Im getting a NullReferenceException at the part where the lINQ statement selects and creates the new Loaded Customer object.
I have already made sure the build for the XML documents are content and copyToOutputDirectory is set to if newer
this is the Exception & Also the var value is null so its definitely not loading for some reason:
SOLVED: The lesson I learnt here is that you need to pay close attention to your XML and data. If some of your XML data has empty values, then you need to account by making sure the select statement can handle it.
Above I had the code
select new XMLOrders
{
OrderID = x.Attribute("ContactName").Value,
CustomerID = x.Attribute("CustomerID").Value,
OrderDate = x.Attribute("OrderDate").Value,
ShippedDate = x.Attribute("ShippedDate").Value,
Freight = x.Attribute("Freight").Value,
ShipName = x.Attribute("ShipName").Value,
ShipCity = x.Attribute("ShipCity").Value,
ShipCountry = x.Attribute("ShipCountry").Value
};
Which really should have included casts to string values so if there is empty data is "" not throwing a null exception.
Secondly I should have been getting the Element values not the Attribute values.
select new XMLOrders
{
OrderID = (string)x.Element("OrderID").Value,
CustomerID = (string)x.Element("CustomerID").Value,
OrderDate = (string)x.Element("OrderDate").Value,
ShippedDate = (string)x.Element("ShippedDate").Value,
Freight = (string)x.Element("Freight").Value,
ShipName = (string)x.Element("ShipName").Value,
ShipCity = (string)x.Element("ShipCity").Value,
ShipCountry = (string)x.Element("ShipCountry").Value
};
Have you tried not to use join but just:
from customer in LoadXMLCustomers()
from order in LoadXMLOrders()
where customer.CustomerID = order.CustomerID
select new PostXMLJoinOrder
...
Just notice that in such query LoadXMLOrders() can be called once again for each customer. So pre-save it first.
Also don't forget to materialize queries calling ToArray()/ToList().