Using ToString() in LINQ queries? - c#

I have writting a LINQ query to fill a listview but it useses the .ToString() method which apparetly is not allowed. When I use the below code I get the error message:
Error: LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression
Is there a way to use the ToString() in LINQ or if that is not possible what is the solution to converting a DateTime to String in the query. Please knot that ReleaseDateName is a string and ReleaseDate is a DateTime
using (var db = new ReleaseInfo())
{
lvReleaseInfo.DataSource = (from r in db.MediaReleases
join rn in db.ReleaseNames
on new { MediaReleaseID = r.MediaReleaseID, CultureCodeID } equals new { rn.MediaReleaseID, rn.CultureCodeID }
join plat in db.MediaPlatforms
on new { MediaPlatformID = r.MediaPlatformID, CultureCodeID } equals new { plat.MediaPlatformID, plat.CultureCodeID }
join pub in db.MediaPublishers
on new { MediaPublisherID = r.MediaPublisherID, CultureCodeID } equals new { pub.MediaPublisherID, pub.CultureCodeID }
join c in db.Countries
on new { CountryID = r.CountryID, CultureCodeID } equals new { c.CountryID, c.CultureCodeID }
join rd in db.ReleaseDates
on new { MediaReleaseID = r.MediaReleaseID, CultureCodeID } equals new { rd.MediaReleaseID, rd.CultureCodeID }
join a in db.AffiliateLinks
on new { MediaReleaseID = r.MediaReleaseID, CultureCodeID } equals new { a.MediaReleaseID, a.CultureCodeID }
where r.SectionID == SectionID
select new
{
rn.ReleaseTitle,
plat.MediaPlatformName,
pub.MediaPublisherName,
c.CountryName,
ReleaseDate = (rd.ReleaseDate == null ? rd.ReleaseDateName : rd.ReleaseDate.ToString()),
a.AffiliateLinkAddress
}).ToList();
lvReleaseInfo.DataBind();
}

Since you are materializing your query to list anyway, you could do the conversion on the .NET side, rather than in the RDBMS, like this:
...
select new {
rn.ReleaseTitle,
plat.MediaPlatformName,
pub.MediaPublisherName,
c.CountryName,
rd.ReleaseDateName,
rd.ReleaseDate,
a.AffiliateLinkAddress
}).AsEnumerable() // <<== This forces the following Select to operate in memory
.Select(t => new {
t.ReleaseTitle,
t.MediaPlatformName,
t.MediaPublisherName,
t.CountryName,
ReleaseDate = t.ReleaseDateName ?? t.ReleaseDate.ToString()
t.AffiliateLinkAddress
}).ToList();
Since the ToString() is called on an element from IEnumerable<T>, it will no longer fail. Also note the use of ?? operator in place of a null-checking ? : conditional.

The problem is that you can't call ToString() on a field until it's been deserialized. So, rather than trying to call ToString() in the query, simply do it on the results afterwards.
In the database the value you're operating on has no notion of ToString() which is why you get the error. The query may look and feel like C# code but keep in mind that under the covers that is being transformed to a SQL query like any other. After you get the list back you can write a very simple LINQ query to solve the problem.

Related

L2S System.DateTime has no supported translation to SQL

My Data Service is has method of type IQueryable and my controller is trying to convert the date time but I am getting this error. Any help would me great.
Error
Method 'System.String ToCommonDateString(System.Nullable`1[System.DateTime])' has no supported translation to SQL.
data Service
public IQueryable<TemplatesJoinAgent> GetTemplateAgentKeyDiseaseId(Guid? agentKey, Guid? diseaseId)
{
//Common part
var TemplatesJoinAgent = (from t in UnitOfWork.GetRepository<Template>().Get(t => t.IsCurrentVersion && t.Status == (short)TemplateMode.Published)
join r in UnitOfWork.GetRepository<Regimen>().Get() on t.Id equals r.TemplateId
join rp in UnitOfWork.GetRepository<RegimenPart>().Get() on r.Id equals rp.RegimenId
join re in UnitOfWork.GetRepository<RegimenEntry>().Get() on rp.Id equals re.RegimenPartId
join a in UnitOfWork.GetRepository<Agent>().Get() on re.AgentVersionKey equals a.VersionKey
select new TemplatesJoinAgent
{
TemplateId = t.TemplateId,
TemplateTitle = t.Title,
GroupTitle = t.GroupTitle,
GuideLineTitle = t.GuideLineTitle,
ExternalDiseaseId = t.ExternalDiseaseId,
DiseaseName = t.DiseaseName,
VersionKey = t.VersionKey,
AgentRxNormTallMan = a.RxNormTallMan,
AgentNccnTallMan = a.NccnTallMan,
AgentName = a.Name,
AgentVersionKey = a.VersionKey,
Added = t.Added,
Modified = t.Modified,
Indication = t.Indications,
});
TemplatesJoinAgent = TemplatesJoinAgent.Distinct();
return TemplatesJoin
}
controller
PublishedDate = (t.Modified ?? t.Added).ToCommonDateString(),
public static string ToCommonDateString(this DateTime? d)
{
return (d.HasValue ? d.Value.ToCommonDateString() : "N/A");
}
The engine does not know how to translate your custom function to SQL. The simplest way to get around that is to add an AsEnumerable() before your projection that uses the custom function. That changes the context from SQL to in-memory and allows custom functions.
The risk is that you want to make sure you have executed as many of your filters as you can before calling AsEnumerable(), otherwise you'll be pulling back more data than you need and filtering in-memory. Of course, if your filters require custom functions then you'll either have to accept that or change your filter to be SQL-compatible.

Incorporate Enum.GetName(...) into Linq Query

I have the enumeration:
public enum CmdType {
[Display(Name = "abc")]
AbcEnumIdentifier = 0,
[Display(Name = "xyz")]
XyzEnumIdentifier = 1,
...
}
I'd like to get the names of each enumeration into my query, but even using .WithTranslations() I'm getting this error:
LINQ to Entities does not recognize the method 'System.String
GetName(System.Type, System.Object)' method, and this method cannot be
translated into a store expression.
The query:
var joinedRecord =
(
from m in mTable
join b in bTable on m.Id equals b.aRefId
select new {
aId = a.Id,
aAttrib1 = a.Attrib1
...
bCmdType = Enum.GetName(typeof(CmdType), b.CmdType)
}
).WithTranslations();
How do I return a generated value using Enum.GetName(...) within the query?
LINQ to entities tries to translate your query to SQL and it fails to do so, because there is no equivalent of the Enum.GetName method in SQL.
You need to materialize the results of the query and convert the enum values to their name in the memory.
var joinedRecords = (
from m in mTable
join b in bTable on m.Id equals b.aRefId
select new {
aId = a.Id,
aAttrib1 = a.Attrib1
...
bCmdType = b.CmdType
}
).AsEnumerable() //Executes the query, further you have simple CLR objects
.Select(o => new {
aId = o.Id,
aAttrib1 = o.Attrib1
...
bCmdTypeName = Enum.GetName(typeof(CmdType), o.CmdType)
});
You are calling Enum.GetName(typeof(CmdType), b.CmdType) which cannot be translated to SQL as the Enum definition is not in the DB if you take a look at your rows you'll see there is an int instead of the name of the Enum value in question.
Try this:
var joinedRecord =
(
from m in mTable
join b in bTable on m.Id equals b.aRefId
select new {
aId = a.Id,
aAttrib1 = a.Attrib1
...
bCmdType = b.CmdType
}
)
.AsEnumerable() // or ToList()
.Select( // map to another type calling Enum.GetName(typeof(CmdType), b.CmdType) )
.WithTranslations();
What this does is that by calling AsEnumerable() or ToList() you are no longer processing an instance of IQueryable<T> (which is what your original query returns, on the bad side once you do this all returned objects will be on memory). So once you have objects in memory you can use them just as any other C# object which should allow you to use the methods you want.
Try casting to AsEnumerable() so you can use LINQ to Objects. LINQ to Entities will try to translate it to SQL for which there is no equivalent:
var joinedRecord =
(from m in mTable
join b in bTable on m.Id equals b.aRefId)
.AsEnumerable()
.Select(x => new {
aId = a.Id,
aAttrib1 = a.Attrib1
...
bCmdType = Enum.GetName(typeof(CmdType), b.CmdType)
})
.WithTranslations();
See http://www.lavinski.me/ef-linq-as-emumerable/

Type interference in the call join

I have built a short and sweet join query to try and get a feel on how to create a join. I managed to do it in SQL. But I'm not sure how to do it in LINQ.
LINQ:
public IQueryable<DepartmentBreakdownReport> GetDepartmentBreakdown
(int SupplierID, int ReviewPeriodID)
{
return (from detail in camOnlineDb.Details
join suppDepp in camOnlineDb.SuppDepts
on new { detail.ClientID, detail.CategoryID }
equals new { suppDepp.ClientID, suppDepp.CategoryID }
select detail.ClientID + "" + detail.CategoryID);
}
Edit: Ignore the parameters which are brought in, I will cater to those once I have my join working.
You are returning an IQueryable<string> rather than what I assume you want is IQueryable<DepartmentBreakdownReport>. To return that type, you need to project in the select by specifying the type, something like this:
return (from detail in camOnlineDb.Details
join suppDepp in camOnlineDb.SuppDepts
on new { detail.ClientID, detail.CategoryID }
equals new { suppDepp.ClientID, suppDepp.CategoryID }
select new DepartmentBreakdownReport
{
Property1 = detail.Property1,
//your properties here
});
The problem was that Category ID was nullable in Detail but not in suppDepp.
To fix it I changed it from a non-nullable type

Converting DateTime when using LINQ and WebAPI?

I am having problem with converting the DateTime I am collecting from the database to localtime with LINQ. A LINQ query won't let me use ToLocalTime() and I can't seem to get any fix outside the query to work with the anonymous type of list.
Here is the LINQ query from the controller :
// GET: api/Scan
public object Getv_Update_ComplianceStatusAll()
{
var dato = DateTime.Now.AddYears(-1);
var scan = (from n in db.C_RES_COLL_NO9000AC
join s in db.v_Update_ComplianceStatusAll on n.MachineID equals s.ResourceID
join u in db.v_UpdateInfo on s.CI_ID equals u.CI_ID
join c in db.v_CICategoryInfo on u.CI_ID equals c.CI_ID
where (n.MachineID == s.ResourceID) && (u.DateRevised > dato)
group s by new { n.Name } into grp
select new
{
Name = grp.Key.Name,
StatusScan = grp.Max(t=> t.LastStatusCheckTime)
});
return scan;
}
This is my attemt at a fix outside the query :
var newScan = scan.ToList();
foreach (var s in newScan)
{
s.StatusScan = s.StatusScan.ToLocalTime();
}
return newScan;
The converstion works, but it returns "Error 306 Property or indexer 'AnonymousType#1.StatusScan' cannot be assigned to -- it is read only"
So, how do I convert the UTC to local time in the controller (before I return anything to the website)?
Yes, Anonymous Type is handy, but it is a bad idea to return it from a method -- Outsider do not know what actually the object is. It is recommended to create a strong type to store the result and return IEnumerable<ScanItem>. Then you are able to modify the result.
public class ScanItem
{
public string Name { get; set; }
public datetime StatusScan { get; set; }
}
If you must use Anonymous Type, You can build a new list like this
scan.ToList().Select(i => new {Name = i.Name, StatusScan = i.StatusScan.ToLocalTime()});
var newScan = scan.ToList();
foreach (var s in newScan)
{
s.StatusScan = s.StatusScan.ToLocalTime();
}
return newScan;
your newScan is a List, you cannot directly assign
s.StatusScan = s.StatusScan.ToLocalTime();

CRM LINQ Composite join "The method 'Join' is not supported" error

Im getting a "The method 'Join' is not supported" error... Funny thing is that i simply converted the 1st LINQ into the 2nd version and it doesnt work...
What i wanted to have was LINQ version #3, but it also doesnt work...
This works
var query_join9 = from s in orgSvcContext.CreateQuery(ServiceAppointment.EntityLogicalName)
join b in orgSvcContext.CreateQuery(bh_product.EntityLogicalName)
on s["bh_contract"] equals b["bh_contract"]
where ((EntityReference)s["bh_contract"]).Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
This doesn't
var query_join9 = from s in orgSvcContext.CreateQuery(ServiceAppointment.EntityLogicalName)
join b in orgSvcContext.CreateQuery(bh_product.EntityLogicalName)
on new { contractid = s["bh_contract"] }
equals new { contractid = b["bh_contract"] }
where ((EntityReference)s["bh_contract"]).Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
Also, this doesn't, which is a composite join and what i really aim for
var query_join9 = from s in orgSvcContext.CreateQuery(ServiceAppointment.EntityLogicalName)
join b in orgSvcContext.CreateQuery(bh_product.EntityLogicalName)
on new { contractid = s["bh_contract"], serviceid = s["serviceid"] }
equals new { contractid = b["bh_contract"], serviceid = s["serviceid"] }
where ((EntityReference)s["bh_contract"]).Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
I tried early binding and still doesnt work...
var query_join9 = from s in orgSvcContext.CreateQuery<ServiceAppointment>()
join b in orgSvcContext.CreateQuery<bh_product>()
on new { foo = s.bh_contract.Id }
equals new { foo = b.bh_Contract.Id }
where s.bh_contract.Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
stil not working
var query_join9 = from s in orgSvcContext.CreateQuery<ServiceAppointment>()
join b in orgSvcContext.CreateQuery<bh_product>()
on new { s.bh_contract.Id, s.ServiceId }
equals new { b.bh_Contract.Id, ServiceId = b.bh_Service }
where s.bh_contract.Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
But im simply trying to do the example(s) here How to do joins in LINQ on multiple fields in single join
What am i missing?
Thanks in advance
While I'm not entirely sure which CRM you're using, I think you're misunderstanding something.
In order for a LINQ query to work, there needs to be a LINQ provider for the underlying data source -- the bit of code responsible for translating chain of e.g. Join, Where, operator usage, etc, etc, into the query API of the data source. This might be SQL, some custom query language, or some chain of methods.
Two LINQ providers (such as, one for LINQ to DataSet and some custom provider you've written yourself) don't have to support the same methods and other code. The precise subset of LINQ methods (and/or other embedded statements) a LINQ provider supports is dependent on its implementation.
Looking at it like that, it's not that surprising that the LINQ provider you're using doesn't seem to comprehend the standard syntax for joins using multiple fields, or doesn't seem to comprehend the usage of anonymous types at all.
My advice is to search the documentation of the supplied LINQ provider to see which query operations it supports (perhaps there is a note about this specific mode of query not being supported). Failing that, you'll have to resort to some sort of other query -- one not involving an equijoin. Perhaps your best option is to perform the joins separately, and then intersect the two result groups. It really depends on the specifics of the case.
Have you looked at the MSDN samples. There are some multiple-column join examples there:
using (ServiceContext svcContext = new ServiceContext(_serviceProxy))
{
var list_join = (from a in svcContext.AccountSet
join c in svcContext.ContactSet
on a.PrimaryContactId.Id equals c.ContactId
where a.Name == "Contoso Ltd" && <<--- multiple join here
a.Address1_Name == "Contoso Pharmaceuticals"
select a).ToList();
foreach (var c in list_join)
{
System.Console.WriteLine("Account " + list_join[0].Name
+ " and it's primary contact "
+ list_join[0].PrimaryContactId.Id);
}
}
This other thread might be relevant

Categories

Resources