In MVVM, i am getting an entity set from the repository to viewmodel. While trying to access the elements of the entity, it throws an exception:
LINQ to Entities does not recognize the method 'InsurableRisk.Entities.QueriesParameter ElementAt[QueriesParameter](System.Linq.IQueryable`1[InsurableRisk.Entities.QueriesParameter], Int32)' method, and this method cannot be translated into a store expression.
Here is my code:
Repository:
public IQueryable<QueriesParameter> GetParams(int QKey)
{
IQueryable<QueriesParameter> param = (from Q in Context.QueriesParameters
where (Q.QueryKey == QKey)
select Q);
return param;
}
Service:
public IQueryable<QueriesParameter> GetParams(int QKey)
{
return _repository.GetParams(QKey);
}
ViewModel:
paramLabel = new string[] { "ParamLabel1", "ParamLabel2", "ParamLabel3", "ParamLabel4", "ParamLabel5", "ParamLabel6" };
param = new string[] { "Param1", "Param2", "Param3", "Param4", "Param5", "Param6" };
paramVisibility = new string[] { "ParamVisiblity1", "ParamVisiblity2", "ParamVisiblity3", "ParamVisiblity4", "ParamVisiblity5", "ParamVisiblity6" };
paramLabelVisibility = new string[] { "ParamLabelVisiblity1", "ParamLabelVisiblity2", "ParamLabelVisiblity3", "ParamLabelVisiblity4", "ParamLabelVisiblity5", "ParamLabelVisiblity6" };
private Dictionary<int, string> m_queryNames;
private Dictionary<int, string> m_ReadOnlyQueryNames;
private int m_SelectedQueryNames;
public int SelectedQueryNames
{
get
{
return m_SelectedQueryNames;
}
set
{
if (m_SelectedQueryNames != value)
{
m_SelectedQueryNames = value;
OnPropertyChanged("SelectedQueryNames");
var QKey = m_SelectedQueryNames;
var sqlQuery = _service.GetQuery(QKey);
var paramCount = _service.GetParamCount(QKey);
//code to make the run button visible and the parameters to be visible
m_Visibility = true;
IQueryable<QueriesParameter> param = _service.GetParams(QKey);
for (int i = 1; i <= paramCount; i++)
{
OnPropertyChanged(paramLabelVisibility[i]);
OnPropertyChanged(paramVisibility[i]);
QueriesParameter qParam = param.ElementAt(i); <!-- I get the exception here -->
m_LabelName = qParam.ParameterName;
OnPropertyChanged(paramLabel[i]);
//OnPropertyChanged(param[i]);
}
}
}
Any help as in why i am getting this error?
You are getting this error because ElementAt is not supported by LINQ to Entities (indeed, how you will translate it into SQL?). Here is list of Supported and Unsupported LINQ Methods.
You can enumerate over params instead:
IQueryable<QueriesParameter> param = _service.GetParams(QKey);
int i = 1; // btw why you are iterating from index 1? It should be zero!
foreach(var p in param)
{
OnPropertyChanged(paramLabelVisibility[i]);
OnPropertyChanged(paramVisibility[i]);
QueriesParameter qParam = p; // here
m_LabelName = qParam.ParameterName;
OnPropertyChanged(paramLabel[i]);
i++;
}
Another option - move query to client side by calling AsEnumerable() or ToList(). Then Linq to Objects will be used, where you can use ElementAt(index) method or via indexer [index]:
List<QueriesParameter> param = _service.GetParams(QKey).ToList();
//... your code
QueriesParameter qParam = param[i];
Related
I am trying to add a linq line to a MapField (Google protobuf).
My actual code works fine like
private static MapField<string, TestResultProto> dToP (Dictionary<Uri, TestResult> keyValuePairs)
{
MapField<string, TestResultProto> keyValues = new();
foreach (var pair in keyValuePairs)
{
keyValues[pair.key.ToString()] = TRTP (pair.value);
}
return keyValues;
}
private static TestResultProto TRTP (TestResult tr)
{
TestResultProto t = new()
{
id = tr.id;
email = tr.email;
};
return t;
}
And now I use the above methods.
object o = //something;
Class1 c = new Class1();
c.Results.Add(dToP(o.Results)); // This works as expected
//c.Results is a MapField<string, TestResultProto>
//o.Results is Dictionary<Uri, TestResult> keyValuePairs
So, to write the above line, I was thinking to add a linq like :
**c.Results.AddRange(o.Results.Select(TRTP))); //ERROR**
Here, I get the error in the linq (above line), saying no method for AddRange for MapField. So, there is something like AddEntriesFrom, but the signature is it expects Codec. Any idea on how to do it ?
If you're desperate to turn it LINQ you could do something like:
private static MapField<string, TestResultProto> dToP (Dictionary<Uri, TestResult> d)
{
var m = new MapField<string, TestResultProto>();
m.Add(d.ToDictionary(
kvp => kvp.Key.ToString(),
kvp => new TestResultProto
{
id = kvp.Value.id,
email = kvp.Value.email
}
));
return m;
}
I have a grid which shows records from a table. On this grid I am using customized pagination and sorting, so I need to use customized column filtering as well.
var expression = ExpressionBuilder.Expression<EventModel>(request.Filters);
The above code snippet gets filter condition from Kendo Grid in the controller of type System.Linq.Expressions.Expression<Func<EventModel,bool>> expression,
and am converting it to string, and passing it to DAL code as shown below,
string filterExpression = ExpressionBuilder.Expression<EventModel>(request.Filters).ToString();
List<EventModel> eventModelList = new List<EventModel>();
eventModelList = eventComponent.GetEventData(request.PageSize, request.Page, searchstring, sortDirection, sortColumnName, filterExpression, ref recCount);
In the DAL I need to convert filterExpression from string to System.Linq.Expressions.Expression<Func<EventModel,bool>>
var res = eventInfo.AsQueryable().Where(filterExpression);//Gets error here
lstEventInfo = lstEventInfo.AsQueryable().Where(res);
Am getting an error can not convert from string to System.Linq.Expressions.Expression>'.
So could anyone tell me how could I convert a string to System.Linq.Expressions.Expression<System.Func<ModelClass,bool>> type in C#.
Here is a simple example how to create where dynamically.
public class Mock
{
public int Id { get; set; }
public int ForeignId { get; set; }
public decimal Total { get; set; }
}
class Program
{
static void Main(string[] args)
{
var list = new List<Mock>()
{
new Mock{
Id = 1,
ForeignId = 1,
Total = 100,
},
};
var query = list.AsQueryable();
// t
var parameter = Expression.Parameter(typeof(Mock), "t");
// t.Total
var propertyExpression = Expression.PropertyOrField(parameter, "Total");
// 100.00M
var constant = Expression.Constant(100M, typeof(decimal));
// t.Total == 100.00M
var equalExpression = Expression.Equal(propertyExpression, constant);
// t => t.Total == 100.00M
var lambda = Expression.Lambda(equalExpression, parameter);
// calls where.
var whereExpression = Expression.Call(typeof(Queryable), "Where", new[] { query.ElementType }, query.Expression, lambda);
// add where to query.
query = query.Provider.CreateQuery(whereExpression) as IQueryable<Mock>;
Console.ReadKey();
}
}
But You can use this
https://github.com/PoweredSoft/DynamicLinq
here is the Nuget package
https://www.nuget.org/packages/PoweredSoft.DynamicLinq/
There is a litle sample here how to do simple web filtering
https://github.com/PoweredSoft/DynamicLinq#how-it-can-be-used-in-a-web-api
You could adapt it to fit your filter expression model.
[HttpGet][Route("FindClients")]
public IHttpActionResult FindClients(string filterField = null, string filterValue = null,
string sortProperty = "Id", int? page = null, int pageSize = 50)
{
var ctx = new MyDbContext();
var query = ctx.Clients.AsQueryable();
if (!string.IsNullOrEmpty(filterField) && !string.IsNullOrEmpty(filterValue))
query = query.Query(t => t.Contains(filterField, filterValue)).OrderBy(sortProperty);
// count.
var clientCount = query.Count();
int? pages = null;
if (page.HasValue && pageSize > 0)
{
if (clientCount == 0)
pages = 0;
else
pages = clientCount / pageSize + (clientCount % pageSize != 0 ? 1 : 0);
}
if (page.HasValue)
query = query.Skip((page.Value-1) * pageSize).Take(pageSize);
var clients = query.ToList();
return Ok(new
{
total = clientCount,
pages = pages,
data = clients
});
}
An alternative is to use DynamicLinq
https://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library
I wrote this code snippet for converting from string to Expression.
public List<Product> Get(string filter = null)
{
var p = Expression.Parameter(typeof(Product), "x");
var e = (Expression)DynamicExpressionParser.ParseLambda(new[] { p }, null, filter);
var typedExpression = (Expression<Func<Product, bool>>)e;
var res = _productDal.GetList(typedExpression);
return res;
}
I used System.Linq.Dynamic.Core namespace for Asp.Net Core. You can use System.Linq.Dynamic namespace for Asp.Net.
If you use classic Asp.Net instead of Asp.Net Core, you should write
var e = (Expression)DynamicExpression.ParseLambda(new[] { p }, null, filter); instead of
var e = (Expression)DynamicExpressionParser.ParseLambda(new[] { p }, null, filter);
And your string parameter (which is named as a filter) should be like "(x.ProductID > 10)"
If your string parameter is different, you can use following code snippet to convert from expression to string for getting same string parameter into Get method.
public static string Select(this Grid _grid, Expression<Func<Product, bool>> filter = null)
{
//filter = {x => (x.ProductID > 1)}
BinaryExpression be = filter.Body as BinaryExpression;
//be = {(x.ProductID > 1)}
return be.ToString();
}
I have used String Builder to generate a RAW SQL QUERY in C#.
List<string> columns = new List<string>();
columns.Add("id");
columns.Add("Temp");
StringBuilder SqlStatement = new StringBuilder();
SqlStatement.Append("Select ");
for (int i = 0; i < columns.Count; i++)
{
if (i == columns.Count - 1)
{
SqlStatement.Append(columns[i]);
}
else
{
SqlStatement.Append(columns[i]);
SqlStatement.Append(",");
}
}
SqlStatement.Append(" FROM graph_update");
var ctx = new graphDBContext();
var result = ctx.Database.SqlQuery<graphDBContext>(SqlStatement.ToString()).ToList();
This translates into SELECT id,Temp FROM graph_update
And the result gives me
id = 1, temp = 20
id = 2 temp = 30
How do I get all these values????
I'm too use to:
foreach(var item in result)
{
item.id = id;
item.temp = temp;
}
But it won't let me.
EDIT:
Sorry but I'm not sure what you mean. Here is my debugger
Try to use foreach like this if theres no error return
foreach(var v in result)
{
String v0 = v[0].ToString();
String v1 = v[1].ToString();
}
Assuming you have EF > 6, then the ctx.Database.SqlQuery, according to the method documentation:
Creates a raw SQL query that will return elements of the given generic type.
The type can be any type that has properties that match the names of the columns returned from the query, or can be a simple primitive type. The type does not have to be an entity type. The results of this query are never tracked by the context even if the type of object returned is an entity type.
With that in mind you can do something like this:
public class GraphUpdateResult
{
public int Id {get; set;}
public decimal Temp {get; set;}
}
Then in your current method:
var result = ctx.Database.SqlQuery<GraphUpdateResult>SqlStatement.ToString()).ToList();
foreach (var graphResult in result)
{
Console.WriteLine(graphResult.Id);
Console.WriteLine(graphResult.Temp);
}
You can add more columns to the GraphUpdateResult class for EF to bind to, even if in some queries you don't specify them in the select statement.
I hope this helps.
foreach(var item in result)
{
var id = item.id;
var temp = item.temp;
}
in your code above, you are trying to assign the values to the item, instead of retrieving.
You can use a ORM-Mapper like
https://stormy.codeplex.com/SourceControl/latest#Stormy.cs
It is a very light Mapper and you can look how it works.
It maps the reader Data to the Object data:
public class CatMapper : ISelectable<Cat>
{
public Cat ApplySelect(IDataReader reader)
{
return new Cat()
{
Name = reader["name"].ToString(),
Weight = (float)reader["weight"]
};
}
}
In my controller I'm using following code to return the 2 lists to ajax request:
public JsonResult getdata(int seat_plane_id)
{
int lid;
layoutsController L = new layoutsController();
JsonResult result = L.getlayouts(seat_plane_id);
List<layouts> L1 = (List<layouts>)result.Data;
List<SeatPlans>[] allUser = new List<SeatPlans>[2];
for(int i=0; i<L1.Count; i++)
{
String lid1 = L1[i].ticket_no_start;
lid = Int32.Parse(lid1);
allUser[i]= new List<SeatPlans>();
allUser[i]= db.SEATPLAN.Where(d => d.layout_id == lid).ToList();
}
var v = new { allUser0 = allUser[0], allUser1 = allUser[1] ,allUser2= allUser[2] };
return Json(v, JsonRequestBehavior.AllowGet);
}
I'm catching the returned value in ajax request as:
success: function (v) {
loadData(v.allUser0);
loadData(v.allUser0);
}
But my problem is: I will have a dynamic size of allUser (size is L1.Count). And so I will get L1.Count no of lists. So I need to create var v={ } dynamically. How to do this? If you have any other solution, it is acceptable.
Simply make v a dictionary. The serializer will generate identical JSON for you as you would have had with the dynamic object.
var v = new Dictionary<string, SeatPlans>();
int id = 0;
foreach (SeatPlans sp in allUser)
{
v.Add($"allUser{id}", sp);
id++;
}
return Json(v, JsonRequestBehavior.AllowGet);
In the following method I am trying to fetch the Username by passing the id value where the ids passed as parameter can be multiple values as in csv's (eg: 1,2) and are returned to the calling function as IEnumerable.
Code Follows as below :
[NonAction]
public static IEnumerable<UserProfile> SearchCMSAdmins(string s)
{
//var searchResults = Entities.UserProfiles.Where(item =>item.UserName.Contains(s));
//return searchResults;
string[] ids = s.Split(',');
IEnumerable<UserProfile> results = null;
IList<UserProfile> user = new List<UserProfile>();
for (int i = 0; i < ids.Length; i++)
{
int id = Convert.ToInt32(ids[i].ToString());
var entity = Entities.UserProfiles.Where(item => item.UserId);
//user.Add(entity);
results = results.Concat(entity);
}
return results;
}
Any help is appreciated.
Try using Contains:
var results = Entities.UserProfiles.Where(item => ids.Contains(item.UserId));
http://msdn.microsoft.com/en-us/library/ms132407.aspx
You can get the id array to be of int type, You can either use int.TryParse or Convert.ToInt32 like:
int[] ids = s.Split(',').Select(r=> Convert.ToInt32(r)).ToArray();
Later you can modify your LINQ query as:
IList<UserProfile> user = Entities.UserProfiles
.Where(item=> ids.Contains(item)).ToList();
This would be like Select * from table where ID in (1,2,3) see Creating IN Queries With Linq to SQL for idea
[NonAction]
public static IEnumerable<UserProfile> SearchCMSAdmins(string s)
{
string[] ids = s.Split(',');
foreach (string idAsString in ids)
{
int id = Convert.ToInt32(idAsString);
var entity = Entities.UserProfiles.Where(item => item.UserId == id);
yield return entity;
}
}
should do it (there should be some validation code too in case the id is not an int or the entity is null)