Member is "not supported in LINQ to Entities" - c#

So I am new to C#, LINQ, and MVC. I am trying to get a list of Ages, but it says
The specified type member 'Age' is not supported in LINQ to Entities.
Only initializers, entity members, and entity navigation properties
are supported.
For a previous tutorial, they use this exact same logic, except they check a string, not an int (Age). Why is this giving me a fit, and how can I fix it?
public ActionResult SearchIndex(string ageValue, string searchString)
{
if (!string.IsNullOrEmpty(ageValue))
{
var AgeList = new List<string>();
var AgeListQry = from d in db.Actors orderby d.Age select d.Age.ToString();
AgeList.AddRange(AgeListQry.Distinct());
}
// other stuff
}
I want to learn what is going on, so that I can avoid this in the future!
Entity Model code
public class Actor
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime BirthDate { get; set; }
public int Age
{
get {
return (int)(DateTime.Now - BirthDate).TotalDays / 365;
}
}
public decimal NetValue { get; set; }
}
public class ActorDBContext : DbContext
{
public DbSet<Actor> Actors { get; set; }
}

As mentioned in the comments, you can't call ToString() in a Linq to Entities query. Instead do it like this:
var AgeList = new List<string>();
//retrieve as whatever type Age is, no conversion in SQL Server
var AgeListQry = (from d in db.Actors orderby d.Age select d.Age).ToList();
//convert them after the fact, using Linq to Objects
AgeList.AddRange(AgeListQry.Select(a => a.ToString()).Distinct());
EDIT
I saw your latest update that does show that Age is not a database column. You are then required to do something like this (assuming BirthDate is properly mapped):
var AgeList = new List<string>();
//retrieve BirthDate from SQL Server and use ToList() to get it to run immediately
var AgeListQry = (from d in db.Actors orderby d.BirthDate select d.BirthDate).ToList();
//convert them after the fact, using Linq to Objects
AgeList.AddRange(AgeListQry.Select(bd => ((int)(DateTime.Now - bd).TotalDays / 365).ToString()).Distinct());
Linq to Entities maps your expressions to SQL statements and there is nothing for it to map to when you use your Age property. Instead, you need to get what you can from SQL Server (BirthDate) and then do the translation to Age yourself. You could replace the inline code with a method call like this if you'd rather:
AgeList.AddRange(AgeListQry.Select(bd => CalculateAge(bd)).Distinct());
//...
private string CalculateAge(DateTime birthday)
{
return ((int)(DateTime.Now - bd).TotalDays / 365).ToString();
}

You haven't the Age in you DB scheme and it is impossible to convert LINQ to DB query.
You must order the Age collection in client side or add calculated column to your table.

There is another way. Have a converter file, where you pass the object, works with the birthdate and produces the age, returns the same object. That also means, that you can't search the database for the age column

Related

Update model sort order [duplicate]

Any idea why the LINQ OrderBy is not working in following code, (have no errors but method does not sort ...)
First my own type
public class IQLinksView
{
public int id { get; set; }
public int catid { get; set; }
public int? viewed {get;set;}
public string name {get;set;}
public string desc {get;set;}
public string url {get;set;}
public string pic {get;set;}
public string cat {get;set;}
}
then query :
IQueryable<IQLinksView> newView =
from links in this.emContext.tbl_otherlinks
select new IQLinksView { id = links.pklinkid, catid =
links.tbl_catgeory.pkcategoryid, viewed = links.linkviewed, name = links.linkname,
desc = links.linkdesc, pic = links.linkpicture, url = links.linkurl, cat =
links.tbl_catgeory.categoryname };
Untill here all fine :-), but then
newView.OrderBy(x => x.viewed);
just changes nothing,... Page is loading results showing ... but no ordering ... sniff
i have Try with (creating a comparer object ... ):
newView.OrderBy(x => (Int32)x.viewed, new CompareIntegers());
same result, no ordering ...
I do have workarounds but just wondering what is missing ....
Any suggestions will be appreciated thanks a lot :-)
Don't throw away the return value. The OrderBy extension method is does not mutate the input. Try:
newView = newView.OrderBy(x => x.viewed);
There is no reason why that won't work, assuming the viewed value is correct. Also, make sure that OrderBy is after any operations (e.g. Distinct) which will ruin ordering.
Happy coding!
No-Tracking Queries
Consider use the asnotracking() after orderby() if the result is a readonly result.
Example:
query = query.OrderByDescending(x => x.Rating).AsNoTracking();

Linq to entities - reusing predicates with navigation properties?

I am trying to find a way to reuse predicates for filtering entities in EF 6.1.3. I've run into a problem filtering related properties using 'Where'.
E.g. if I have this interface IValidFromTo
public interface IValidFromTo
{
DateTime StartDate { get; set;}
DateTime EndDate { get; set; }
}
and a function that returns a predicate for Where :
public class Extensions
{
public static Expression<Func<T, bool>> Current<T>()
where T : IValidFromTo
{
var currentDate = DateTime.Now;
return x => x.StartDate <= currentDate && x.EndDate >= currentDate;
}
}
See http://www.albahari.com/nutshell/predicatebuilder.aspx for background.
When applied directly to DbSet, this method works.
var query = ctx.Items.Where(Extensions.Current<Item>()); // compiles
But how to make it work with a more complex query dealing with navigation properties?
E.g. if I have a DbSet<Person> with a collection of Item:
public class Person
{
...
public virtual ICollection<Item> Items { get; set; }
}
and I want to project it into an object containing the name of the person and just the current Items, I end up with some rather cluttered code:
var relationQuery = ctx.People.Select(x => new
{ Name = x.Name,
CurrentItems = x.Items.AsQueryable().Where(Extensions.Current<Item>())
});
I wonder if it is possible to improve this code, e.g. to be able to write something like
CurrentItems = x.Items.Current() // quasi an extension method on `ICollection<Item>`?
(writing an extension method on ICollection<IValidFromTo> doesn't work, because EFf wouldn't recognize this method and throw an error)
UPDATE
Seems like this is achievable via a Join (supposing that each Person can only have a single valid item):
var isCurrent= x => <<some condition on x>>;
...
var validItems = ctx.Items.Where(isCurrent);
var peopleWithCurrentItems = from person in ctx.Persons
join item in validItems on person.Id equals item.Owner.Id
select new { Person = person, Item = item };
If there may be more than one valid Item per Person, then
var grouped = peopleWithValid.GroupBy(x => x.Person);
However, this version of the query will exclude persons with no matching Items.

Issue with Filtering Entity Framework data by Calculated Fields

Here's a simplified EF Scenerio of my issue:
public partial class MyClass
{
public int ID { get; set; }
public byte Month { get; set; }
public int Year { get; set; }
public DateTime CalculatedDate
{
get
{
return new DateTime(this.Year, this.Month, 1);
}
}
}
I'm using a repository pattern to access these objects, which is also implementing a Where(predicate) method that returns an IEnumrable, just like LINQ. it is being used like this:
var myClasses = myClassRepo.Where(mc=> mc.ID > 10);
this works well and returns the expected objects with all fields, including CalculatedDate.
HOWEVER, when i try to use the calculated field as part of the predicate like this:
var myClasses = myClassRepo.Where(mc=> mc.CalculatedDate == DateTime.Now);
I Receive an error:
Object reference not set to an instance of an object.
I know I can "walkaround" this by retrieving a first set of results, and then filtering it further by the calculated field. but I'm trying to understand why this is happening and what could be done to fix this.
Entity Framework tries to convert your LINQ to SQL, so it has trouble with trying to convert your MyClass.CalculatedDate method into something recognizable in SQL. You may be able to get around this by adding an .AsEnumerable() call in your LINQ before your where, like so:
var myClasses = myClassRepo.AsEnumerable().Where(mc => mc.CalculatedDate == DateTime.Now);
I think it's because EF doesn't support querying on custom properties, as they cannot be translated into a DB column

Adding custom value in a LINQ join

I have two lists with me:
FXCashFlow [contains - amount, paymentdate, TradeId, Currency]
FXTrades [Contains - TradePreferences, TradeId]
What I need to have in the return class is:
Return Object [Type, amount, paymentdate, TradeId, Currency, TradePreference]
Where Type = "Fx", as the data is fetched from Fx class.
For Return Object, I am using a LINQ JOIN like this:
var list = _fxCashflow.GetAll().Join(_fxTrade.GetAll(),
outerKey => outerKey.TradeId,
innerKey => innerKey.TradeId,
(CashFlow, Trade) => new
{
//"Fx", <- This line gives error
CashFlow.TradeId,
Trade.TradeReference,
CashFlow.PaymentAmount,
CashFlow.CurrencyCode,
CashFlow.PaymentDate,
CashFlow.CashflowTypeCode
}
);
I need to insert "Fx", because this data will be concatenated to a class where this "Fx" will identify the records returning from cashflow class.
How can I insert a custom value in this returning object? Or if there's any other way to do this?
Much Appreciated!!
Try to insert it like this instead:
(CashFlow, Trade) => new
{
Type = "Fx",
CashFlow.TradeId,
Trade.TradeReference,
CashFlow.PaymentAmount,
CashFlow.CurrencyCode,
CashFlow.PaymentDate,
CashFlow.CashflowTypeCode
}
Wouldn't it make more sense to introduce an actual Fx class rather than using a string identifier?
public class Fx
{
public int TradeId { get; set; }
public string TradeRef { get; set; }
public decimal PaymentAmount { get; set; }
...
}
(CashFlow, Trade) => new Fx
{
TradeId = CashFlow.TradeId,
TradeRef = Trade.TradeReference,
PaymentAmount = CashFlow.PaymentAmount,
...
}
You can store this information in enum and use it inside your anonymous object or you can define a new property for the anonymous object.

LINQ Update or Insert objects into List

Ok, so I have a List that contains a collection of message objects. An updated list of message objects comes in every 60 seconds. Some of the objects in the first collection will have updated data based on an ID property inside each object.
public class Message
{
public Int64 Id { get; set; }
public string Property1 { get; set; }
public DateTime MessageDate { get; set; }
}
How in LINQ can I Insert in the updated object based on the Id?
You need to get a reference to the object Message.
Something like this:
this.Messages.ForEach(mess => {
if(mess.Id == someValue){
// do something here
}
})
List<Message> LocalList;
List<Message> ArrivingList;
var mergedItems = LocalList.Contcat(ArrivingList);
mergedItems = (from msg in mergedItems
group msg by msg.Id into grp
let sameKey = mergedItems.Where(obj => obj.Id == grp.Id)
select sameKey.Where(obj => obj.MessageDate == grp.Max(obj2 => obj2.MessageDate)).Single()).ToList();
LocalList = (List<Message>)mergedItems;
I think something similar to the above would probably "work" but I would just use a standard Dictionary /List and write a small updating routine. Just because you CAN do something with a particular tool does not mean you SHOULD.
For me LINQ based stuff can be MUCH harder to troubleshoot, understand, debug, trace, evaluate, etc.
(Replace LocalList and ArrivingList in the above example with whatever your actual variable are)

Categories