Good Morning!
Given:
public class FooClass
{
public void FooMethod()
{
using (var myEntity = new MyEntity)
{
var result = myEntity.MyDomainEntity.Where(myDomainEntity => myDomainEntity.MySpecialID > default(int)).Distinct(new FooComparer);
}
}
}
public class FooComparer : IEqualityComparer<MyEntity.MyDomainEntity>
{
public bool Equals(MyEntity.MyDomainEntity x, MyEntity.MyDomainEntity y)
{
return x.MySpecialID == y.MySpecialID;
}
public int GetHashCode(MyEntity.MyDomainEntity obj)
{
return obj.MySpecialID.GetHashCode();
}
}
This will compile, but on runtime I will get an Linq to Entity could not translate Comparer-Exception.
Any suggestions?
If you're providing your own comparisons, you'll need to execute the Distinct call in .NET code. To make sure that happens, use AsEnumerable to turn IQueryable<T> into IEnumerable<T>:
var result = myEntity.MyDomainEntity
.Where(myDomainEntity => myDomainEntity.MySpecialID > default(int))
.AsEnumerable()
.Distinct(new FooComparer());
Of course at that point you'll be pulling more data across from the database. An alternative is to group the data instead:
var result = from entity in myEntity.MyDomainEntity
where entity.MySpecialID > 0
group entity by entity.MySpecialID into groups
select groups.FirstOrDefault();
That will get you the first entity encountered with each ID (assuming my query-fu isn't failing me). That's basically what Distinct does anyway, but it's all at the database.
(Note to future readers: calling First() makes more sense than FirstOrDefault(), but apparently that doesn't work.)
Related
I have problems designing LINQ query for EntityFramework that combines multiple conditions on attached entities with OR condition.
My classes (simplified):
public class EventMessage : EntityBase
{
public IList<EventParameter> Parameters { get; set; }
}
public class EventParameter : EntityBase
{
public string Name { get; set; }
public string Value { get; set; }
}
The goal is to obtain EventMessages which have at least one EventParameter which Value and Name equals one of the passed arguments (passed to query below by QueryParameters of type IList<EventParameter>). The problems is that I want my query to be dynamic, ie. it would be able to generate condition independently on QueryParameters.Count()
I have tried following approaches:
//Throws NotSupportedException when generating SQL
//Message: "Unable to create a constant value of type 'EventParameter'. Only primitive types or enumeration types are supported in this context."
DataContext.Queryable<EventMessage>()
.Where(em => QueryParameters.Any(qp => em.Parameters.Any(p => p.Name == qp.Name && p.Value == qp.Value)));
//Throws NotSupportedException when generating SQL
//Message: "LINQ to Entities does not recognize the method 'FilterByParameters'"
DataContext.Queryable<EventMessage>().Where(em => FilterByParameters(em, QueryParameters));
public bool FilterByParameters(EventMessage eventMessage, IList<EventParameter> queryParameters)
{
bool result = false;
foreach (EventParameter queryParam in queryParameters)
{
result |= eventMessage.Parameters.Any(x => x.Name == queryParam.Name && x.Value == queryParam.Value);
}
return result;
}
Of course, it would be no problem to design query for given number of QueryParameters - I could join OR-conditions within and expression inside .Where(), however I would like to have a query that work independently on paramemeters' count. Is it possible? Using EF 6.1.3.
You should be able to use Union.
DataContext.Queryable<EventMessage>().Where(p => p.Bla == blub).Where(p => something else)
will be an AND. For or you should be able to do something like
DataContext.Queryable<EventMessage>().Where(p => p.Bla == blub).Union(p => something else)
EDIT:
If it is not clear right away: You can combine these filters like this:
IQueryable<...> ApplyOrFilter(IQueryable<...> query, ...)
{
return query.Union(...);
}
I have these code(this is only example code)
public SomeService ()
{
public Queryable<CarDto> GetDtos()
{
context.Cars.Select(c => new CarDto
{
CarDtoId = c.Id,
Name = c.Name,
Status = GetCarStatus(c)
})
}
private CarStatusEnum GetCarStatus(Car car)
{
if(car.statusId == 2 || car.statusId == 3)
{
return 4;
}
return 5;
}
}
This code throw exception(LINQ to Entities does not recognize method GetCarStatus).
I know:
why it throws
I can do ToList() but I need Iqueryble
I can write method code inline without using method
But I want know way How can I do so,that LINQ to Entities can recognize my method?
Not sure if LINQ to Entities is able to convert such function to SQL query. You might need to use Expression for such cases.
Let the DTO do the conversion:
class CarDto
{
...
public int StatusId { get; set; }
public CarStatusEnum CarStatus
{
get { return StatusId == 2 || StatusId == 3
? CarStatusEnum.Four
: CarStatusEnum.Five; }
}
}
So in the query you just copy the TypeId value from the Car. And in the application you access CarStatus.
Note that you should return CarStatusEnum members, not plain integers (even when they match CarStatusEnum values. These may change in the future, which won't break your code, but will surely cause interesting bugs).
So, I'm beggining to use EF, and I'm developing an application using it as ORM. The thing is I haven't had much time to dig into the documentation (I Plan to, in due time)and I'm kind of lost in some things. For example, I have these two queries:
public static int GetNextPadlockNumber()
{
LockersDBEntities1 entities = new LockersDBEntities1();
var query = (from p in entities.PadLocks select p.PadlockNumber).DefaultIfEmpty(0).Max();
return (int)query + 1;
}
public static Data.PadLock GetPadLockByNumber(int number)
{
Data.LockersDBEntities1 entities = new LockersDBEntities1();
var query = (from p in entities.PadLocks where p.PadlockNumber == number select p).FirstOrDefault();
return query;
}
and
public static int GetNextLockerNumber()
{
LockersDBEntities1 entities = new LockersDBEntities1();
var query = (from l in entities.Lockers select l.LockerNumber).DefaultIfEmpty(0).Max();
return (int)query+1;
}
public static Data.Locker GetLockerByNumber(int number)
{
Data.LockersDBEntities1 entities = new LockersDBEntities1();
var query = (from l in entities.Lockers where l.LockerNumber == number select l).FirstOrDefault();
return query;
}
And the thing is They're Exactly the same query. Isn't there Any way to do this without having to specify that I want a locker or a padlock? Thanks in advance heroes.
You could have used the LockerNumber and PadlockNumber as Identity Autoincrement fields since that is what you are doing, and then named them the same (Id for instance).
And that way the need to "get next locker number" is no longer necessary since every new entity inserted will get the next available number and with a Repository pattern you could have those methods as common methods in the Base Repository class as something like this:
public IEntity GetEntityById(int number)
{
return ObjectSet.Single(x => x.Id == number);
}
One of the great things with EF is that you can do things like that. It's not trivial, and it will take some trial and error, but you can abstract a lot of the database complexities away.
Now, assuming that LockersDBEntities1 is a DbContext, you could something like this:
public static class LockersDBEntities1Extensions
{
public static int GetNextNumber<T>(
this LockersDBEntities1 context,
Expression<Func<T, int>> getNumberExpression)
{
var query = (from item in context.Set<T>
select getNumberExpression(item))
.DefaultIfEmpty(0)
.Max();
return (int)query + 1;
}
}
and use it like:
int nextPadlockNumber = new LockersDBEntities1()
.GetNextNumber<Padlock>(p => p.PadlockNumber)
and
int nextPadlockNumber = new LockersDBEntities1()
.GetNextNumber<Locker>(l => l.LockerNumber)
The getNumberExpression expression is needed because there is no common way to access the number across all entities. It might be overdesign, but if that is an issue I would do something like this:
//this should be a better name, to reflect the real scenarion
interface ILockNumberProvider
{
int Number {get; }
}
and implement that interface on Locker, Padlock and other classes that need to provide lock numbers. Then, in the method above the expression can be omitted, and a generic constraint can be used, like:
public static class LockersDBEntities1Extensions
{
public static int GetNextNumber<T>(this LockersDBEntities1 context)
where T:ILockNumberProvider
{
var query = (from item in context.Set<T>
select item.Number)
.DefaultIfEmpty(0)
.Max();
return (int)query + 1;
}
}
I have a LINQ to entities model with Table Per Hierarchy inheritance. I have a query over the base type, and I want to do specific type-dependent logic. For example:
IQueryable<BaseType> base = ...
// this works fine
var result = base.Select(b => b is DerivedType1 ? 1 : 2).ToList();
// this doesn't compile to SQL
var result2 = base.Select(b => b is DerivedType1 ? ((DerivedType1)b).DerivedProperty : null).ToList();
Is there any way to do something like this without processing IQueryables of each derived type separately:
// I'd rather not do this:
var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty);
var resultB = base.OfType<DerivedType2>().Select(d => default(int?));
var result = resultA.Concat(resultB).ToList();
Direct casting to an entity type like (DerivedType1)b isn't supported with LINQ-to-Entities but the as operator (b as DerivedType1) is, hence you could try:
var result2 = base
.Select(b => b is DerivedType1
? (b as DerivedType1).DerivedProperty
: null)
.ToList();
OfType<DerivedType1>()
will return an IEnumerable, if possible, try to change to base-type to IEnumerable instead of IQueryable, you might en up in some SQL restrictions when using IQueryable.
That is of course if you are not actually quering a database?
You can use EntityFramework.Extended to improve the performance of the query instead of doing 2 round trips to DB.
var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty).Future();
var resultB = base.OfType<DerivedType2>().Select(d => default(int?)).Future();
var result = resultA.Concat(resultB).ToList();
In this case only one round trip to bd is executed.
This framework is very useful for many other things int EF
You could have a method on your base type that's overridden in your derived types to provide the relevant property value.
public class MyBaseClass
{
public virtual int GetSomething()
{
throw new NotImplementedException();
}
}
public class MyDerivedClass1 : MyBaseClass
{
public int SomeProperty { get; set; }
public override int GetSomething()
{
return this.SomeProperty;
}
}
public class MyDerivedClass2 : MyBaseClass
{
public int SomeOtherProperty { get; set; }
public override int GetSomething()
{
return this.SomeOtherProperty;
}
}
Then you could:
var result = base.Select(b => b.GetSomething()).ToList();
Try this, I have never done anything with needing to do this kind of this but this should do it. Also if you use base, first of all don't because it is a keyword but if you must, use #base the # in front of the name denotes that it is not used as a keyword.
var resultA = base.Select(aVar =>
(aVar is DerivedType1) ?
(DerivedType)(((DerivedType1)aVar).DerivedProperty)
:
(DerivedType)(default(int?))
).ToList();
Let's say I have the following class :
public class Person {
public string FirstName { get; set; }
public string SurName { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
}
Also, I have the following method and I am reaching out to person data via a repository.
public IEnumerable<Person> getPeople(string searchField, string searchTerm) {
//_repo.GetAll() returns IEnumerable<Person>
var model = _repo.GetAll();
//Need the logic here for filtering
return model;
}
As you can see I am getting two parameter for the method : searchField and searchTerm.
searchField is for the field name whose value will be used for filtering. searchTerm is the value which will be used to compare with retrived value (sorry if I am not clear here but this is the most I can come up with)
What I would normally do is as follows :
public IEnumerable<Person> getPeople(string searchField, string searchTerm) {
//_repo.GetAll() returns IEnumerable<Person>
var model = _repo.GetAll();
switch(searchField) {
case "FirstName":
model = model.Where(x => x.FirstName == searchTerm);
break;
case "SurName":
model = model.Where(x => x.SurName == searchTerm);
break;
//Keeps going
}
return model;
}
Which will work just fine. But if I make a change on my class, this code will have a change to break or be in lack of some functions if I add new properties this class.
What I am looking for is something like below :
NOTE :
This below code completely belongs to my imagination and there is no such a
thing exists.
model = model.Where(x => x.GetPropertyByName(searchField) == searchTerm);
Am I flying too high here if it is impossible or being complete idiot if there is already a built in way for this?
Looks like you need Dynamic Linq queries:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
I use this extension method to achieve what you want.
public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source, string propertyName, string value)
{
Expression<Func<TEntity, bool>> whereExpression = x => x.GetType().InvokeMember(propertyName, BindingFlags.GetProperty, null, x, null).ObjectToString().IndexOf(value, StringComparison.InvariantCultureIgnoreCase) >= 0;
return source.Where(whereExpression);
}
Note: ObjectToString is just another extension method that returns string.Empty if the Object passed in is NULL
For linq2Object You can use reflection as bellow(it's not very fast):
model.Where(x => x.GetType().GetProperty(propName).GetValue(x, null) == propVal);
but for linq2Entity I think this doesn't work, it works for linq2objects.
I think the following implementation looks an awful lot like what you originally intended, although changing this to a generic method likely makes more sense.
public IEnumerable<Person> getPeople(string searchField, string searchTerm) {
PropertyInfo getter=typeof(Person).GetProperty(searchField);
if(getter==null) {
throw new ArgumentOutOfRangeException("searchField");
}
return _repo.GetAll().Where(x => getter.GetValue(x, null).ToString()==searchTerm);
}
This should be type-safe:
public IEnumerable<T> Where<T,U>(Func<T,U> propertySelector, U value)
{
return model.Where(x => propertySelector(x) == value);
}
usage:
Where((MyClass x) => x.PropertyName, propertyValue);
Or:
public IEnumerable<T> Where<T>(Func<T,bool> entitySelector)
{
return model.Where(entitySelector);
}
usage:
Where<MyClass>(x => x.PropertyName == propertyValue && x.OtherProperty == otherValue);
Use Reflection
model = model.Where(x =>
((string)x.GetType().GetProperty("searchField").GetValue(0, null)) == searchTerm);
Rather than messing with reflection, custom expression trees, etc., when using Entity Framework, consider using the Builder Method extensions to the standard LINQ operators which take strings rather than lambdas. These are much easier to build for dynamic query requirements:
string filter = String.Format("it.{0} = #value", fieldName);
var model = context.People.Where(filter, new ObjectParameter("value", searchValue));
Of course, this would mean that you yould need to modify your repository to return IObjectSet rather than IEnumerable. It would perform better as well. By returning IEnumerable, you are hydrating every row in your database to your repository and then filtering via LINQ to Objects rather than applying the filter back in your database.
For more information about the Builder Methods in EF, see the BuilderMethodSamples.cs in http://archive.msdn.microsoft.com/EFQuerySamples/Release/ProjectReleases.aspx?ReleaseId=4422.