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..
Related
I have a relational dataset as dummy. I want to take the data as hierarchical (Role > SubRoles > Permissions) then I will convert to JSON but I get an exception:
Error CS0266
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'System.Collections.Generic.List'. An explicit conversion exists (are you missing a cast?)
Thanks for your answer.
Model classes:
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public List<SubRole> SubRoles { get; set; }
}
public class SubRole
{
public int Id { get; set; }
public string Name { get; set; }
public string EndPoint { get; set; }
public List<Permission> Permissions { get; set; }
}
public class RoleSubRole
{
public int Id { get; set; }
public int RoleId { get; set; }
public int SubRoleId { get; set; }
}
public class Permission
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SubRolePermission
{
public int Id { get; set; }
public int SubRoleId { get; set; }
public int PermiisonId { get; set; }
}
public class RoleModel
{
public Role Role { get; set; }
}
Program class:
public static void Main(string[] args)
{
RoleModel roleModel = new RoleModel()
{
Role =
(from u in DataSet.Users
join r in DataSet.Roles on u.RoleId equals r.Id
where u.Id == 1
select new Role
{
Name = r.Name,
SubRoles =
(from rsb in DataSet.RoleSubRoles
join sr in DataSet.SubRoles on rsb.SubRoleId equals sr.Id
where r.Id == rsb.RoleId
select new SubRole
{
Name = sr.Name,
EndPoint = sr.EndPoint,
Permissions =
(from srp in DataSet.SubRolePermissions
join p in DataSet.Permissions on srp.PermiisonId equals p.Id
where srp.SubRoleId == sr.Id
select new Permission
{
Name = p.Name
})
})
})
};
}
You should use
Permissions =
(from srp in DataSet.SubRolePermissions
join p in DataSet.Permissions on srp.PermiisonId equals p.Id
where srp.SubRoleId == sr.Id
select new Permission
{
Name = p.Name
}).ToList()
The Permissions is a List<T>, while the query returns an IEnumerable<T>. Same is the case with SubRoles.You need to convert to List<T>, which could be done using the ToList() method
Complete Query
Role =
(from u in DataSet.Users
join r in DataSet.Roles on u.RoleId equals r.Id
where u.Id == 1
select new Role
{
Name = r.Name,
SubRoles =
(from rsb in DataSet.RoleSubRoles
join sr in DataSet.SubRoles on rsb.SubRoleId equals sr.Id
where r.Id == rsb.RoleId
select new SubRole
{
Name = sr.Name,
EndPoint = sr.EndPoint,
Permissions =
(from srp in DataSet.SubRolePermissions
join p in DataSet.Permissions on srp.PermiisonId equals p.Id
where srp.SubRoleId == sr.Id
select new Permission
{
Name = p.Name
}).ToList()
}).ToList()
}).First()
};
Also note that the Role represents a single entity, while the query returns a collection. You need to make a choice on which entity from the collection needs to be stored. For the sample code above, I have used First()
I'm trying to do a left join that is 1 user record has 1 (if it's there) contact Record associated with it. When this runs, it still pull multiple contacts ignoring my condition of 'where c.RecordType == "USR"'.
public class Users
{
public int Id { get; set; }
... other properties
public Contact Contact { get; set; }
}
public class Contact
{
public int Id { get; set; }
public int RecordID { get; set; }
public string RecordType { get; set; }
[ForeignKey("RecordID")]
public Users User { get; set; }
}
Linq Query
var query3 = (from c in db1.Contacts
join u in db1.Users
on c.RecordID equals u.Id
into a
from b in a.DefaultIfEmpty(new Models.Users())
where c.RecordType == "USR"
&& b.Lastname.Contains(name)
&& b.Active == 1
select new
{
b.Id,
b.FirstName,
b.Lastname,
b.FullName,
b.Contact
}).ToList();
Any help with where I'm going wrong would be much appreciated.
Thanks,
Adam
You can do a join in linq without using the join keyword. I use the following construct for left joins and do not have any issues.
var query3 = (from c in db1.Contacts
from u in db1.Users.Where(x => c.RecordID == x.Id).DefaultIfEmpty()
where (c.RecordType == "USR")
&& u.Lastname.Contains(name) && (u.Active == 1)
select new
{
u.Id,
u.FirstName,
u.Lastname,
u.FullName,
u.Contact
}).ToList();
As per information in question
1 user record has 1 (if it's there) contact Record associated with it
that means, a user can have zero or more contacts, so you suppose to use User as main entity and then left join on Contacts. Also you should apply Contact type filter during JOIN itself, like below
var query3 = ( join u in db1.Users
from c in db1.Contacts
on new { Record = c.RecordID, Type = c.RecordType } equals new { Record = u.Id, Type = "USR"} into b
from cont in b.DefaultIfEmpty(new Models.Contacts())
where u.Lastname.Contains(name)
&& u.Active == 1
select new
{
u.Id,
u.FirstName,
u.Lastname,
u.FullName,
cont.Contact
}).ToList();
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 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();
Trying To Left Join 2 Tables
public IEnumerable<APPLICANT> GetApplicant()
{
IEnumerable<APPLICANT> applicantdata = Cache.Get("applicants") as IEnumerable<APPLICANT>;
if (applicantdata == null)
{
var applicantList = (from a in context.Profiles
join app in context.APPLICANTs
on a.PROFILE_ID equals app.Profile_id into joined
from j in joined.DefaultIfEmpty()
select new
{
Profiles = a,
APPLICANTs= j,
}).Take(1000);
applicantdata = applicantList.AsQueryable().OrderBy(v => v.APPLICANT_ID).ToList();
if (applicantdata.Any())
{
Cache.Set("applicants", applicantdata, 30);
}
}
return applicantdata;
}
But the problem is that im having error at the last Line
applicantdata = applicantList.AsQueryable().OrderBy(v => v.APPLICANT_ID).ToList();
The error says
Error 1 Cannot implicitly convert type 'System.Collections.Generic.List<AnonymousType#1>' to 'System.Collections.Generic.IEnumerable<Models.APPLICANT>'. An explicit conversion exists (are you missing a cast)
THIS IS MY APPLICANT CLASS
[DataContract(IsReference = true)]
public partial class APPLICANT
{
[DataMember]
public int APPLICANT_ID { get; set; }
[DataMember]
public string APPLICANT_LastName { get; set; }
[DataMember]
public string APPLICANT_FirstName { get; set; }
[DataMember]
public string APPLICANT_MiddleName { get; set; }
[DataMember]
public string APPLICANT_Address { get; set; }
[DataMember]
public string APPLICANT_City { get; set; }
[DataMember]
public string APPLICANT_Phone { get; set; }
[DataMember]
public string APPLICANT_Email { get; set; }
[DataMember]
public Nullable<int> Profile_id { get; set; }
It is the type of the collections that is the problem.
IEnumerable<APPLICANT> applicantdata ...
is not equal to the type you get from this expression:
var applicantList =
(from a in context.Profiles
join app in context.APPLICANTs
on a.PROFILE_ID equals app.Profile_id into joined
from j in joined.DefaultIfEmpty()
select new //<-- this is what dicides the type of the applicationList
{
Profiles = a,
APPLICANTs= j,
}).Take(1000);
this mean that you cannot do this:
applicantdata = applicantList...
I think you need to do something like this:
applicantdata = applicantList
.SelectMany(c => c.APPLICANTs) //this line added
.AsQueryable().OrderBy(v => v.APPLICANT_ID).ToList();
UPDATE
If you are using my "solution" with SelectMany, you should be asking yourself - "Am I extracting the right data when I create applicationList..?"
perhaps this is better..:
var applicantList =
(from a in context.Profiles
join app in context.APPLICANTs
on a.PROFILE_ID equals app.Profile_id into joined
from j in joined.DefaultIfEmpty()
select j //<-- this is APPLICANTs type
}).Take(1000);
I think your issue is not converting from a list to an IEnumerable. Instead, the problem is that you cannot convert an LIST of annonoymous object to an IEnumerable of APPLICANT.
You should modify your select statement so you're selecting an APPLICANT.