Why Linq to Entities cannot convert this call to SQL? - c#

var providerTypes = from provider_types in dbContext.vw_LookUpProviderType
select new vw_LookUpProviderType
{
ApplicationUser = provider_types.ApplicationUser,
ProviderTypeId = provider_types.ProviderTypeId,
Type = provider_types.Type
};
var query =
from providerCoi in dbContext.Provider_COI
where providerCoi.COIProviderId == message.ProviderId
join providerDetail in dbContext.ProviderDetail
on providerCoi.ProviderId equals providerDetail.ProviderId
into providerDetails
from providerDetail in providerDetails.DefaultIfEmpty()
select new Result
{
PhysicianAdvisorId = providerCoi.ProviderId,
HasConflictOfInterest = providerCoi.COIFlag == true,
PhysicianAdvisorName = providerDetail.FirstName + " " + providerDetail.MiddleName + " " + providerDetail.LastName,
ProviderType = providerTypes
.Where(providerType => providerDetail.ProviderTypeIds.Contains(providerType.ProviderTypeId.ToString()))
.Select(providerType => providerType.Type)
.ToArray<string>()
.Aggregate((current, next) => current +", " + current)
};
I have selected the providerTypes. There is table providerDetails, and and there there is field providerTypeIds - this is can't be changed. For example providerTypes: [1: 'Type 1', 2: 'Type 2', 3: 'Type 4'] and providerTypeIds: '1,2,':
Select from providerTypes that types that can be found in providerIdsString
providerTypes.Where(providerType => providerDetail.ProviderTypeIds.Contains(providerType.ToString())) // => [1: 'Type 1', 2: 'Type 2']
Than select on string representations: .Select(providerType => providerType.Type) // => ['Type 1', 'Type 2']
And finally transform all of them into string of types separated by comma:
.ToArray<string>()
.Aggregate((current, next) => current +", " + current) // => 'Type 1, Type 2
And this is throws an exception
LINQ to Entities does not recognize the method 'System.String
Aggregate[String](System.Collections.Generic.IEnumerable1[System.String],
System.Func3[System.String,System.String,System.String])'

So, as was mentioned in comments, Aggregate function is not supported in Linq to Entities, see ref for more details: https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/supported-and-unsupported-linq-methods-linq-to-entities#aggregate-methods
Unfortunetely simple toList, didn't help me, I don't know the certain reason, but maybe it because of use field for search - providerTypes, maybe toList on the providerTypes could help, but I've used another workaround - select physicianIds string, and then manualy transform it. See code below.
//select provider types lookup, from there can map id with it text meaning, f.e.[{providerTypeId: 1, type: 'Value 1' },{providerTypeId: 2, type: 'Value 2' }]
var providerTypes = exUrDataContext.vw_LookUpProviderType.ToList();
//select physicianCois with unmodified providerTypeIds(f.e '1,2,')
var physicianCois =
await (from providerCoi in dbContext.Provider_COI
where providerCoi.COIProviderId == message.PhysicianId
join providerDetail in exUrDataContext.vw_ProviderDetail
on providerCoi.ProviderId equals providerDetail.ProviderId
into providerDetails
from providerDetail in providerDetails.DefaultIfEmpty()
select new Result
{
PhysicianAdvisorId = providerCoi.ProviderId,
HasConflictOfInterest = providerCoi.COIFlag == true,
PhysicianAdvisorName = providerDetail.FirstName + " " + providerDetail.MiddleName + " " + providerDetail.LastName,
ProviderType = providerDetail.ProviderTypeIds
}).ToListAsync();
//map string with ids to string from text values, f.e. '1,2' => 'Value 1, Value 2'
foreach (var physicianCoi in physicianCois)
{
string physicianProviderNames = "";
foreach (var providerKey in physicianCoi.ProviderType.Split(new char[] { ',' }, System.StringSplitOptions.RemoveEmptyEntries))
{
if (!physicianProviderNames.Equals(""))
{
physicianProviderNames += ", ";
}
physicianProviderNames += providerTypes.Where(providerType => providerType.ProviderTypeId == int.Parse(providerKey)).FirstOrDefault().Type;
}
physicianCoi.ProviderType = physicianProviderNames;
}

Related

Always Encrypted Operand Clash In Query

I've recently started to use Always Encrypted with SQL Server 2016 to encrypt sensitive information.
For the most part, everything has been plain sailing with regards to seamless transition.
One thing has cropped up though, the following error -
Operand type clash: nvarchar(255) encrypted with (encryption_type =
'RANDOMIZED', encryption_algorithm_name =
'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name =
'CEK_AutoK2', column_encryption_key_database_name = 'hrsys') is
incompatible with nvarchar Statement(s) could not be prepared.
The method that is throwing this exception is the following -
public ActionResult GetStaffList(string term)
{
var staffs = from e in db.Staffs
where e.FirstName.Contains(term) || e.LastName.Contains(term)
&& e.EmploymentStatus == "Active"
select new
{
label = e.FirstName + " " + e.LastName,
id = e.Id
};
return (Json(staffs, JsonRequestBehavior.AllowGet));
}
But, if I modify the method, to this-
public ActionResult GetStaffList(string term)
{
var staffSearch= db.Staffs
.ToList()
.Where(e => e.FirstName.Contains(term) || e.LastName.Contains(term))
.Where(e => e.EmploymentStatus == "Active");
var staffs = from e in staffSearch
select new
{
label = e.FirstName + " " + e.LastName,
id = e.Id
};
return (Json(staffs, JsonRequestBehavior.AllowGet));
}
It doesn't throw an error.
Is there anyway of consolidating the two 'query' variables to a single one, making sure that the data is returned as JSON with the following block -
select new
{
label = e.FirstName + " " + e.LastName,
id = e.Id
}
Also what I cant get my head around is that when querying with 'from e .....' it throws an error, but when querying with 'db.Staff ...' it doesn't.

c# elastic search nest how to output the document score

I would like to output the score for each result from elastic search. But I'am unsure how I can get this.
Below is my current code for running a query:
var searchResults = client.Search<Place>(s => s
.From(0)
.Size(5)
.Explain(true)
.TrackScores(true)
.Query(q => q
.QueryString(fqqs1 => fqqs1
.OnFieldsWithBoost(d => d
.Add("name", 5.0)
)
.Query("west midlands birmingham")
)
)
.Sort(sort => sort.OnField("_score").Descending())
.Sort(sort => sort.OnField(f => f.id).Ascending())
);
// Output the results to console
Console.WriteLine("\nTotal Hits: " + searchResults.HitsMetaData.Hits.Count + " out of " + searchResults.HitsMetaData.Total);
List<Result> results = new List<Result>();
foreach (Place result in searchResults.Documents)
{
results.Add(new Result
{
woeid = Convert.ToInt32(result.id),
name = result.name,
admin1 = result.admin1,
admin2 = result.admin2,
type = result.type
});
Console.WriteLine(result.id + " > " + result.name + " > " + result.admin1 + " > " + result.admin2 + " > " + result.type);
}
use the .Hits property collection on the ISearchResponse<T> - The collection contains the score for each document in the .Score property, as well as the document in the .Source property.
You can sort by score. eg: Sort(sort => sort.OnField("_score").Descending())
var result = client.Search(q => q
.Index(your-index-name)
.From(0)
.Type("post")
.Fields("firstName","LastName")
.TrackScores(true)
.Size(12)
.Query(SearchQuery)
.Sort(sort => sort.OnField("_score").Descending())
);
Code sample for Nest 7
var sorts = new List<ISort>();
sorts.Add(new FieldSort { Field = "_score", Order = SortOrder.Descending });
var searchRequest = new SearchRequest<ElasticIndexGroupProduct>()
{
Profile = true,
From = (pageNumber - 1) * pageSize,
Size = pageSize,
Version = true,
Sort = sorts,
Query = new MatchAllQuery()
Aggregations = aggrigations
};
var searchResponse = _client.Search<ElasticIndexGroupProduct>(searchRequest);

LINQ to Entities does not recognize the method 'System.String GetMonthName(Int32)' method

What am I doing wrong?
FYI The repository method GetResearchArticles() returns an IQueryable.
var grouped = (from p in _researchArticleRepository.GetResearchArticles()
group p by new { month = p.DatePublished.Month, year = p.DatePublished.Year } into d
select new
{
dt = d.Key.month + "-" + d.Key.year,
dtByMonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(d.Key.month) + " " + d.Key.year
}//count = d.Count() }
)
.OrderByDescending(g => g.dt);
return grouped.ToDictionary(item => item.dt, item => item.dtByMonthName);
It is probably trying to convert that statement to a SQL expression, which it cannot do. Instead of trying to do that on the database, you should make that call on data that has already been retrieved from the database.
Basically, you need to run .ToList() or something to force the fetching of the data, and then make a call to GetMonthName()
since your not doing any filtering this should work but your pulling out all research articles into memory because SQL doesnt understand how to get month
var grouped = (from p in _researchArticleRepository.GetResearchArticles().ToList()
group p by new { month = p.DatePublished.Month, year = p.DatePublished.Year } into d
select new
{
dt = d.Key.month + "-" + d.Key.year,
dtByMonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(d.Key.month) + " " + d.Key.year
}//count = d.Count() }
)
.OrderByDescending(g => g.dt);
return grouped.ToDictionary(item => item.dt, item => item.dtByMonthName);

Linq Lambda get two properties as string from aggretation

Inside a linq query to an anonymous select I want to concatenate strings from two properties.
For instance to find the full name of the oldest person in some grouping of persons.
var personsAndOldest = db.Persons.GroupBy(person => person.SomeThingThatCanBeGroupedForPerson).Select(a => new
{
FirstName = a.FirstOrDefault().FirstName,
LastName = a.FirstOrDefault().LastName,
BirthDate = a.FirstOrDefault().BirthDate,
FullnameOfOldes = a.Aggregate((pers1, pers2) => pers1.BirthDate > pers2.BirthDate ? pers1 : pers2).FirstName + " " //How do I get LastName of the old one (without using the full aggregate again)
});
Do I have to write the full aggregation again to get the LastName after the firstname and whitespace?
You could use a lambda statement in the Select:
var personsAndOldest = db.Persons.GroupBy(person => person.SomeThingThatCanBeGroupedForPerson).Select(a =>
{
var first = a.First();
var oldest = a.Aggregate((pers1, pers2) => pers1.BirthDate > pers2.BirthDate ? pers1 : pers2);
return new
{
FirstName = first.FirstName,
LastName = first.LastName,
BirthDate = first.BirthDate,
FullnameOfOldes = oldest.FirstName + " " + oldest.LastName)
};
});
You can do this as
var personsAndOldest = db.Persons
.GroupBy(person => person.SomeThingThatCanBeGroupedForPerson)
.Select(g => new
{
a = g.First(),
o = g.Aggregate((pers1, pers2) =>
pers1.BirthDate > pers2.BirthDate ? pers1 : pers2)
})
.Select(pair => new
{
FirstName = pair.a.FirstName,
LastName = pair.a.LastName,
BirthDate = pair.a.BirthDate,
FullnameOfOldes = pair.o.FirstName + " " + pair.o.LastName
});
You can use let to introduce new range variables.
You don't need to specify property name for anonymous type if it equals name of assigned property
I think OrderBy will find oldest person (but you can use aggregate and compare performance)
I believe that oldest person is one with minimal birth date, so you need to change aggregation to pers1.BirthDate < pers2.BirthDate ? pers1 : pers2
So
var personsAndOldest = from p in db.Persons
group p by p.SomeThingThatCanBeGroupedForPerson into g
let first = g.FirtOrDefault()
let oldest = g.OrderBy(x => x.BirthDate).FirstOrefault()
select
{
first.FirstName,
first.LastName,
first.BirthDate,
FullnameOfOldes = oldest.FirstName + " " + oldest.LastName
};

LINQ sorting anonymous types?

How do I do sorting when generating anonymous types in linq to sql?
Ex:
from e in linq0
order by User descending /* ??? */
select new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = string.Format("{0:d}", e.Date)
}
If you're using LINQ to Objects, I'd do this:
var query = from e in linq0
select new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = e.Date.ToString("d")
} into anon
orderby anon.User descending
select anon;
That way the string concatenation only has to be done once.
I don't know what that would do in LINQ to SQL though...
If I've understood your question correctly, you want to do this:
from e in linq0
order by (e.User.FirstName + " " + e.User.LastName).Trim()) descending
select new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = string.Format("{0:d}", e.Date)
}
Would this work, as a way of avoiding Jon's select...into?
from e in linq0
let comment = new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = string.Format("{0:d}", e.Date)
}
orderby comment.User descending
select comment
I'm going to get a necromancer badge for this answer, but I still think it's worth showing this snippet.
var records = await (from s in db.S
join l in db.L on s.LId equals l.Id
where (...)
select new { S = s, Type = l.MyType }
).ToListAsync();
//Data is retrieved from database by now.
//OrderBy below is LINQ to Objects, not LINQ to SQL
if (sortbyABC)
{
//Sort A->B->C
records.OrderBy(sl => sl.Type, new ABC());
}
else
{
//Sort B->A->C
records.OrderBy(sl => sl.Type, new BAC());
}
ABC and BAC implement IComparer<MyType>.

Categories

Resources