this method cannot be translated into a store expression [duplicate] - asp.net-mvc

I saw this code work with LINQ to SQL but when I use Entity Framework, it throws this error:
LINQ to Entities does not recognize the method 'System.Linq.IQueryable'1[MyProject.Models.CommunityFeatures] GetCommunityFeatures()' method, and this method cannot be translated into a store expression.`
The repository code is this:
public IQueryable<Models.Estate> GetEstates()
{
return from e in entity.Estates
let AllCommFeat = GetCommunityFeatures()
let AllHomeFeat = GetHomeFeatures()
select new Models.Estate
{
EstateId = e.EstateId,
AllHomeFeatures = new LazyList<HomeFeatures>(AllHomeFeat),
AllCommunityFeatures = new LazyList<CommunityFeatures>(AllCommFeat)
};
}
public IQueryable<Models.CommunityFeatures> GetCommunityFeatures()
{
return from f in entity.CommunityFeatures
select new CommunityFeatures
{
Name = f.CommunityFeature1,
CommunityFeatureId = f.CommunityFeatureId
};
}
public IQueryable<Models.HomeFeatures> GetHomeFeatures()
{
return from f in entity.HomeFeatures
select new HomeFeatures()
{
Name = f.HomeFeature1,
HomeFeatureId = f.HomeFeatureId
};
}
LazyList is a List that extends the power of IQueryable.
Could someone explain why this error occurs?

Reason:
By design, LINQ to Entities requires the whole LINQ query expression to be translated to a server query. Only a few uncorrelated subexpressions (expressions in the query that do not depend on the results from the server) are evaluated on the client before the query is translated. Arbitrary method invocations that do not have a known translation, like GetHomeFeatures() in this case, are not supported.
To be more specific, LINQ to Entities only support Parameterless constructors and Initializers.
Solution:
Therefore, to get over this exception you need to merge your sub query into the main one for GetCommunityFeatures() and GetHomeFeatures() instead of directly invoking methods from within the LINQ query. Also, there is an issue on the lines that you were trying to instantiate a new instance of LazyList using its parameterized constructors, just as you might have been doing in LINQ to SQL. For that the solution would be to switch to client evaluation of LINQ queries (LINQ to Objects). This will require you to invoke the AsEnumerable method for your LINQ to Entities queries prior to calling the LazyList constructor.
Something like this should work:
public IQueryable<Models.Estate> GetEstates()
{
return from e in entity.Estates.AsEnumerable()
let AllCommFeat = from f in entity.CommunityFeatures
select new CommunityFeatures {
Name = f.CommunityFeature1,
CommunityFeatureId = f.CommunityFeatureId
},
let AllHomeFeat = from f in entity.HomeFeatures
select new HomeFeatures() {
Name = f.HomeFeature1,
HomeFeatureId = f.HomeFeatureId
},
select new Models.Estate {
EstateId = e.EstateId,
AllHomeFeatures = new LazyList<HomeFeatures>(AllHomeFeat),
AllCommunityFeatures = new LazyList<CommunityFeatures>(AllCommFeat)
};
}
More Info: Please take a look at LINQ to Entities, what is not supported? for more info.
Also check out LINQ to Entities, Workarounds on what is not supported for a detailed discussion on the possible solutions.
(Both links are the cached versions because the original website is down)

Related

return LINQ Query result in IList and read it another class [duplicate]

This question already has answers here:
Working with C# Anonymous Types
(8 answers)
Closed 8 years ago.
I am using ASP.NET mvc 5. I have one class that holds all the LINQ which can access to another class. now i convert the LINQ query to list variable Query and returning as IList... the i create object of this class--> call the method and get result.
now i can see in debugging object names but i can't see in foreach loop. my list hold mix data types, plus result is merging from different tables...
public IList GetAllFeeZonesForFeeSchemeByID(int FeeSchemeID)
{
using (var db = new QualificationContext())
{
var Query = from a in db.FeeScheme
join b in db.FeeZoneSchema.Where(c => c.FeeSchemeID == 1) on a.FeeSchemeID equals b.FeeSchemeID
join c in db.FeeZone on b.FeeZoneID equals c.FeeZoneID
select new
{
FeeScheme = a.FeeSchemeID,
FeeZone = b.FeeZoneID,
FeeZone_Description = c.FeeZoneDescription
};
return Query.ToList();
}
}
in controller class...
foreach(var item in obj1.GetAllFeeZonesForFeeSchemeByID(1))
{
item.???? (can't access the object here....
}
many thanks
You should return Generic IList of concrete (not anonymous class):
public IList<FeeSchemeModel> GetAllFeeZonesForFeeSchemeByID(int FeeSchemeID)
{
using (var db = new QualificationContext())
{
var Query = from a in db.FeeScheme
join b in db.FeeZoneSchema.Where(c => c.FeeSchemeID == 1) on a.FeeSchemeID equals b.FeeSchemeID
join c in db.FeeZone on b.FeeZoneID equals c.FeeZoneID
select new FeeSchemeModel
{
FeeScheme = a.FeeSchemeID,
FeeZone = b.FeeZoneID,
FeeZone_Description = c.FeeZoneDescription
};
return Query.ToList();
}
}
public class FeeSchemeModel
{
public int FeeScheme{get;set;}
public int FeeZone{get;set;}
public string FeeZone_Description{get;set;}
};
But I recommend to use IEnumerable<T> instead of IList<T> and use ToArray() method instead of ToList() method if you don't use special features of List<T> (such as method Add())
IList is non-generic interface, it contains only non-generic IEnumerable definition, which enumerates objects. So type of item will be object. That's why you can see only members of System.Object class.
You should either cast item to appropriate type or use generic collection parametrized with appropriate type. But you can't use neither of these approaches while you are using anonymous objects, because you don't know anonymous type name. So, you need to create some class which you will be able to cast to:
foreach(Foo item in obj1.GetAllFeeZonesForFeeSchemeByID(1))
Or use as parameter of method return type:
public IList<Foo> GetAllFeeZonesForFeeSchemeByID(int FeeSchemeID)
One more option is usage of dynamic type, which will resolve operations on object at runtime. You still will not be able to use IntelliSense but your code will work:
foreach(dynamic item in obj1.GetAllFeeZonesForFeeSchemeByID(1))
{
// use item.FeeScheme
}

Entity Framework eager loading navigation property causes error when using user-defined type

Some background
I'm wanting to bind a list of objects (my model-view) to a grid. The model-view contains fields for both an specific entity and fields from a joined entity.
I was getting an error when I would try to bind due to the dbContext being out of scope. I realized I needed to use the .Include() method in order to eager load my navigation property. However, I suspect that since I'm using Linq to Entities, that I'm now generating another error:
"Unable to cast the type 'System.Linq.IQueryable1' to type 'System.Data.Objects.ObjectQuery1'. LINQ to Entities only supports casting EDM primitive or enumeration types."
My code is shown below, any ideas of what I need to do here?
Thanks in advance!
public static List<PlanViewModel> GetPlans()
{
using (var context = new RepEntities())
{
var query = (from p in context.Plans
join r in context.RealEstateDetails on p.ReId equals r.ReId
select new PlanViewModel
{
PlanName = p.PlanName,
TargetCompletionDate = p.TargetCompletionDate,
ActualCompletionDate = p.ActualCompletionDate,
Provision = p.Provision,
StatusTypeId = p.StatusTypeId,
StatusCommon = p.StatusCommon,
Building = r.BuildingName,
City = r.City,
Country = r.Country
}).Include("StatusCommon");
return query.ToList();
}
}
You are almost there, just put Include("StatusCommon") right after context.Plans. Because you need to include StatusCommon before the iteration, this way you can set StatusCommon value for every iteration.
public static List<PlanViewModel> GetPlans()
{
using (var context = new RepEntities())
{
var query = (from p in context.Plans.Include("StatusCommon")
join r in context.RealEstateDetails on p.ReId equals r.ReId
select new PlanViewModel
{
PlanName = p.PlanName,
TargetCompletionDate = p.TargetCompletionDate,
ActualCompletionDate = p.ActualCompletionDate,
Provision = p.Provision,
StatusTypeId = p.StatusTypeId,
StatusCommon = p.StatusCommon,
Building = r.BuildingName,
City = r.City,
Country = r.Country
}).toList();
return query;
}
}

Passing query parameters in Dapper using OleDb

This query produces an error No value given for one or more required parameters:
using (var conn = new OleDbConnection("Provider=..."))
{
conn.Open();
var result = conn.Query(
"select code, name from mytable where id = ? order by name",
new { id = 1 });
}
If I change the query string to: ... where id = #id ..., I will get an error: Must declare the scalar variable "#id".
How do I construct the query string and how do I pass the parameter?
The following should work:
var result = conn.Query(
"select code, name from mytable where id = ?id? order by name",
new { id = 1 });
Important: see newer answer
In the current build, the answer to that would be "no", for two reasons:
the code attempts to filter unused parameters - and is currently removing all of them because it can't find anything like #id, :id or ?id in the sql
the code for adding values from types uses an arbitrary (well, ok: alphabetical) order for the parameters (because reflection does not make any guarantees about the order of members), making positional anonymous arguments unstable
The good news is that both of these are fixable
we can make the filtering behaviour conditional
we can detect the category of types that has a constructor that matches all the property names, and use the constructor argument positions to determine the synthetic order of the properties - anonymous types fall into this category
Making those changes to my local clone, the following now passes:
// see https://stackoverflow.com/q/18847510/23354
public void TestOleDbParameters()
{
using (var conn = new System.Data.OleDb.OleDbConnection(
Program.OleDbConnectionString))
{
var row = conn.Query("select Id = ?, Age = ?", new DynamicParameters(
new { foo = 12, bar = 23 } // these names DO NOT MATTER!!!
) { RemoveUnused = false } ).Single();
int age = row.Age;
int id = row.Id;
age.IsEqualTo(23);
id.IsEqualTo(12);
}
}
Note that I'm currently using DynamicParameters here to avoid adding even more overloads to Query / Query<T> - because this would need to be added to a considerable number of methods. Adding it to DynamicParameters solves it in one place.
I'm open to feedback before I push this - does that look usable to you?
Edit: with the addition of a funky smellsLikeOleDb (no, not a joke), we can now do this even more directly:
// see https://stackoverflow.com/q/18847510/23354
public void TestOleDbParameters()
{
using (var conn = new System.Data.OleDb.OleDbConnection(
Program.OleDbConnectionString))
{
var row = conn.Query("select Id = ?, Age = ?",
new { foo = 12, bar = 23 } // these names DO NOT MATTER!!!
).Single();
int age = row.Age;
int id = row.Id;
age.IsEqualTo(23);
id.IsEqualTo(12);
}
}
I've trialing use of Dapper within my software product which is using odbc connections (at the moment). However one day I intend to move away from odbc and use a different pattern for supporting different RDBMS products. However, my problem with solution implementation is 2 fold:
I want to write SQL code with parameters that conform to different back-ends, and so I want to be writing named parameters in my SQL now so that I don't have go back and re-do it later.
I don't want to rely on getting the order of my properties in line with my ?. This is bad. So my suggestion is to please add support for Named Parameters for odbc.
In the mean time I have hacked together a solution that allows me to do this with Dapper. Essentially I have a routine that replaces the named parameters with ? and also rebuilds the parameter object making sure the parameters are in the correct order.
However looking at the Dapper code, I can see that I've repeated some of what dapper is doing anyway, effectively it each parameter value is now visited once more than what would be necessary. This becomes more of an issue for bulk updates/inserts.
But at least it seems to work for me o.k...
I borrowed a bit of code from here to form part of my solution...
The ? for parameters was part of the solution for me, but it only works with integers, like ID. It still fails for strings because the parameter length isn't specifed.
OdbcException: ERROR [HY104] [Microsoft][ODBC Microsoft Access Driver]Invalid precision value
System.Data.Odbc. OdbcParameter.Bind(OdbcStatementHandle hstmt,
OdbcCommand command, short ordinal, CNativeBuffer parameterBuffer, bool allowReentrance)
System.Data.Odbc.OdbcParameterCollection.Bind(OdbcCommand command, CMDWrapper cmdWrapper, CNativeBuffer parameterBuffer)
System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior behavior, string method, bool needReader, object[] methodArguments, SQL_API odbcApiMethod)
System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior behavior, string method, bool needReader)
System.Data.Common.DbCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
Dapper.SqlMapper.QueryAsync(IDbConnection cnn, Type effectiveType, CommandDefinition command) in SqlMapper.Async.cs
WebAPI.DataAccess.CustomerRepository.GetByState(string state) in Repository.cs
var result = await conn.QueryAsync(sQuery, new { State = state });
WebAPI.Controllers.CustomerController.GetByState(string state) in CustomerController .cs
return await _customerRepo.GetByState(state);
For Dapper to pass string parameters to ODBC I had to specify the length.
var result = await conn.QueryAsync<Customer>(sQuery, new { State = new DbString { Value = state, IsFixedLength = true, Length = 4} });

LINQ To Entities cannot generate string MVC Razor

When Using LINQ to get information I use the following method
var salt_water_disposals = _SaltWaterDisposals.SaltWaterDisposals.Select(sw => new SaltWaterModel
{
saltwater_ID = sw.SaltWaterDisposalID,
lease = sw.Lease.LeaseName,
field = sw.Lease.Field.Name.ToLower(),
permitter_fluid = sw.PermittedFluids.Count == 0 ? "None" : sw.PermittedFluids.Aggregate("", (current, c) => current+c.Fluid.Name),
max_gas_pressure = sw.MaxGasInjectionPressure,
});
However when I run the code I get the following error because of the PermittedFluids:
LINQ to Entities does not recognize the method 'System.String
Aggregate[PermittedFluid,String](System.Collections.Generic.IEnumerable1[FieldPro.Domain.Entities.PermittedFluid],
System.String,
System.Func3[System.String,FieldPro.Domain.Entities.PermittedFluid,System.String])'
method, and this method cannot be translated into a store expression.
I have tried using it also as a ToString() but I get the same error.
Any Ideas?
Any Help would be appreciated.
As you may know, LINQ translates your code into a SQL statement. It can't translate all code, and this is one of those cases.
System.Data.Objects.SqlClient.SqlFunctions is a class that has many static methods that are valid within the LINQ context.
I fixed this by taking out the Aggregate call and calling it in my view, and changing the data type or permitter_fluid.
So My model has:
public System.Data.Objects.DataClasses.EntityCollection<FieldPro.Domain.Entities.PermittedFluid> permitter_fluid { get; set; }
My Controller has:
permitter_fluid = sw.PermittedFluids,
and my view has (using a Html.Grid from MvCContrib):
column.For(m => m.permitter_fluid.Aggregate("", (current, c) => current + c.Fluid.Name)).Named("Permitted Fluid");
This solved my problem

How to lower number of queries that EF4 sends to DB?

I use the following code to remove all KlientDoTrasa assigned to trasaOriginal and then create and assign new KlientDoTrasa based on collection: trasaToEdit.Klienci. In my test case assigned KlientDoTrasa and new KlientDoTrasa are the same, so I thought EF4 should not send any query to database, but it sends, first deletes then inserts. Is there a way to limit it?
public void Edit(int trasaId, TrasaEditViewModel trasaToEdit)
{
Trasa trasaOriginal = _trasaRepository.FindById(trasaId);
trasaOriginal.Nazwa = trasaToEdit.Trasa.Nazwa;
foreach(KlientDoTrasa kdt in trasaOriginal.KlientDoTrasa.ToList())
{
_klientDoTrasaRepository.Remove(kdt);
}
for(int i = 0; i < trasaToEdit.Klienci.Count; i++)
{
var kdt = new KlientDoTrasa {Trasa = trasaOriginal, KlientId = trasaToEdit.Klienci[i].Id, Seq = i};
_klientDoTrasaRepository.Add(kdt);
}
_klientDoTrasaRepository.SaveChanges();
_trasaRepository.SaveChanges();
}
Entity Framework does not do a deep comparison of your entities to determine whether they are equivalent. Your code above will delete then insert, assuming that the two entities are different.
If you want to avoid updating an Entity that hasn't changed, you'll need to check for that in your own logic. I believe you could use a partial class declaration to implement IEqualityComparer for your entities, then compare before submit.

Resources