Dynamically select columns in runtime using entity framework - c#

I have an existing function like this
public int sFunc(string sCol , int iId)
{
string sSqlQuery = " select " + sCol + " from TableName where ID = " + iId ;
// Executes query and returns value in column sCol
}
The table has four columns to store integer values and I am reading them separately using above function.
Now I am converting it to Entity Framework .
public int sFunc(string sCol , int iId)
{
return Convert.ToInt32(TableRepository.Entities.Where(x => x.ID == iId).Select(x => sCol ).FirstOrDefault());
}
but the above function returns an error
input string not in correct format
because it returns the column name itself.
I don't know how to solve this as I am very new to EF.
Any help would be appreciated
Thank you

Not going to be useful for the OP 8 years later, but this question has plenty of views, so I thought it could be helpful for others to have a proper answer.
If you use Entity Framework, you should do Linq projection (Select()), because that leads to the correct, efficient query on the db side, instead of pulling in the entire entity.
With Linq Select() you normally have to provide a lambda, though, so having your your column/property name in a string poses the main difficulty here.
The easiest solution is to use Dynamic LINQ (EntityFramework.DynamicLinq Nuget package). This package provides alternatives to the original Linq methods, which take strings as parameters, and it translates those strings into the appropriate expressions.
Example:
async Task<int> GetIntColumn(int entityId, string intColumnName)
{
return await TableRepository.Entities
.Where(x => x.Id == entityId)
.Select(intColumnName) // Dynamic Linq projection
.Cast<int>()
.SingleAsync();
}
I also made this into an async call, because these days all database calls should be executed asynchronously. When you call this method, you have to await it to get the result (i.e.: var res = await GetIntColumn(...);).
Generic variation
Probably it's more useful to change it into an extension method on IQueryable, and make the column/property type into a generic type parameter, so you could use it with any column/property:
(Provided you have a common interface for all your entities that specifies an Id property.)
public static async Task<TColumn> GetColumn<TEntity, TColumn>(this IQueryable<TEntity> queryable, int entityId, string columnName)
where TEntity : IEntity
{
return await queryable
.Where(x => x.Id == entityId)
.Select(columnName) // Dynamic Linq projection
.Cast<TColumn>()
.SingleAsync();
}
This is called like this: var result = await TableRepository.Entities.GetColumn<Entity, int>(id, columnName);
Generic variation that accepts a list of columns
You can extend it further to support selecting multiple columns dynamically:
public static async Task<dynamic> GetColumns<TEntity>(this IQueryable<TEntity> queryable, int entityId, params string[] columnNames)
where TEntity : IEntity
{
return await queryable
.Where(x => x.Id == entityId)
.Select($"new({string.Join(", ", columnNames)})")
.Cast<dynamic>()
.SingleAsync();
}
This is called like this: var result = await TableRepository.Entities.GetColumns(id, columnName1, columnName2, ...);.
Since the return type and its members are not known compile-time, we have to return dynamic here. Which makes it difficult to work with the result, but if all you want is to serialize it and send it back to the client, it's fine for that purpose.

This might help to solve your problem:
public int sFunc(string sCol, int iId)
{
var _tableRepository = TableRepository.Entities.Where(x => x.ID == iId).Select(e => e).FirstOrDefault();
if (_tableRepository == null) return 0;
var _value = _tableRepository.GetType().GetProperties().Where(a => a.Name == sCol).Select(p => p.GetValue(_tableRepository, null)).FirstOrDefault();
return _value != null ? Convert.ToInt32(_value.ToString()) : 0;
}
This method now work for dynamically input method parameter sCol.
Update:
This is not in context of current question but in general how we can select dynamic column using expression:
var parameter = Expression.Parameter(typeof(EntityTable));
var property = Expression.Property(parameter, "ColumnName");
//Replace string with type of ColumnName and entity table name.
var selector = Expression.Lambda<Func<EntityTable, string>>(property, parameter);
//Before using queryable you can include where clause with it. ToList can be avoided if need to build further query.
var result = queryable.Select(selector).ToList();

You have to try with dynamic LINQ. Details are HERE

Instead of passing the string column name as a parameter, try passing in a lambda expression, like:
sFunc(x => x.FirstColumnName, rowId);
sFunc(x => x.SecondColumnName, rowId);
...
This will in the end give you intellisense for column names, so you avoid possible errors when column name is mistyped.
More about this here: C# Pass Lambda Expression as Method Parameter
However, if you must keep the same method signature, i.e. to support other/legacy code, then you can try this:
public string sFunc(string sCol , int iId)
{
return TableRepository.Entities.Where(x => x.ID == iId).Select(x => (string) x.GetType().GetProperty(sCol).GetValue(x)});
}
You might need to adjust this a bit, I didn't have a quick way of testing this.

You can do this:
var entity = _dbContext.Find(YourEntity, entityKey);
// Finds column to select or update
PropertyInfo propertyInfo = entity.GetType().GetProperty("TheColumnVariable");

Related

Entity Framework Core Dynamic Where Clause

I have been searching and I have not seen an example like this.
I want to query a Model table to retrieve all Models from German manufacturers.
My data structure is Model Table has a Navigation Property called "Manufacturer" and an FK called "IdManufacturer"
A Manufacturer Has a Navigation Property called "Origin" and an FK called IdOrigin.
I would like to have a query that includes a where clause something like this:
(m => m.Manufacturer.Origin.Name == "German");
I would like this to be dynamically created at run time. Not only the lookup value might change, but the next time even the fields may change to, for example:
(m.Type.Name == "SUV");
Finally, there will not necessarily be a UI associated with this request, it may be generated in code.
Please don't get too hung up on the business of it, I am hoping this made up example will be simple to understand.
Any suggestions on how to handle this would be greatly appreciated. Any general thoughts on performance would be created.
My solution:
I have a model RouteList, in this model I created Expression:
public static Expression<Func<RouteList, bool>> FilterByDateOrShiftCode(RoutesFilterViewModel filter)
{
Expression<Func<RouteList, bool>> result = rl => true;
if (string.IsNullOrEmpty(filter.ShiftCode))
{
result = rl => rl.RouteListDate.Date >= filter.From.Date && rl.RouteListDate.Date <= filter.To.Date && rl.User.Id == filter.User.Id;
}
else
{
result = rl => rl.ShiftCode == filter.ShiftCode && rl.User.Id == filter.User.Id;
}
Place where I use it:
model.RouteLists = await _context.RouteLists
.Include(rl => rl.Status)
.Include(rl => rl.User)
.Include(rl => rl.RouteListRows).ThenInclude(rlr => rlr.Address)
.Where(RouteList.FilterByDateOrShiftCode(filter))
.ToListAsync();
Speed of executing grow in several times.
Try the following simple extension method. Note that this realization will work only with sting properties.
Usage:
query = query.Where("Manufacturer.Origin.Name", "German");
Realization:
public static class QueryableExtensions
{
public static IQueryable<T> Where<T>(this IQueryable<T> query, string propPath, string value)
{
var param = Expression.Parameter(typeof(T), "e");
// e.Prop1.Prop2 == value
var body = Expression.Equal(MakePropPath(param, propPath), Expression.Constant(value));
// e => e.Prop1.Prop2 == value
var predicateLambda = Expression.Lambda<Func<T, bool>>(body, param);
return query.Where(predicateLambda);
}
static Expression MakePropPath(Expression objExpression, string path)
{
return path.Split('.').Aggregate(objExpression, Expression.PropertyOrField);
}
}

dynamic query on entity field name in a generic

I'm using generics because I need a lot of reusability with different types of data. my main problem is querying data. I'm looking for a way to query something like this:
public void test<T>(int id, T type) where T : class
{
using (var ctx = myDbContext())
{
var myTbl = ctx.Set<T>();
//this line gets the primary key of the table
string key = myTbl.GetPrimaryKey(ctx);
//this is the query I want:
var myResult = myTbl.FirstOrDefault(x => x.key == id);
//let's say if key = "UserId", then (x => x.UserId == id) or something that translates to this.
}
}
also I have implemented following method:
public object GetPropertyValue(object src, string propertyName)
that I can use to get value of a specific property.
but my problem is that I can't use it inside the .FirstOrDefault() call because of the LINQ to query issues with methods.
I currently use this code instead:
var myResult = myTbl.ToList().FirstOrDefault(x => (int)GetPropertyValue(x, key) == id);
which is fine with a few number of rows in database, but when data grows in future it will have a lot of performance impact.
P.S: I'm using EF power tools for reverse engineering code first
Umm, your code sample is completely unclear. Please, atleast provide correct variable names, because now I don't even know where you use 'key' variable.
Also, if you want to store and query objects and it's properties of various inheritance and nestings, consider to use NoSQL databases instead of relation based SQL engines.
First -> Dont use myTbl.ToList().FirstOrDefault(x => (int)GetPropertyValue(x, key) == id) because it generates a select that brings all rows from that table, then filter by id in memory. You should translate your filter to an Expression> that will generare a select filtered by the Id column.
Build your linq expression like this:
var x = Expression.Parameter(typeof(T), "x");
string keyPropName = type.GetPrimaryKey(ctx);
var equalExp = Expression.Equal(
Expression.Property(x, keyPropName),
Expression.Constant(id)
);
var lambda = Expression.Lambda<Func<T, bool>>(equalExp, x); //x => x.Id == idValue
var myResult = myTbl.FirstOrDefault(lambda);
I used your GetPropertyValue and GetPrimaryKey methods.

How can I refactor this code for LINQ filtering?

Using DataTables (Table plug-in for jQuery) with server side processing I had to create filtering for my data with good performance. I did it however in my opinion it's kinda ugly. A especially part of (.Where(...)) where I have to compare manually each property with search variable. Is it possible to make it better?
using System.Linq.Dynamic; // because of special .OrderBy
public class SomeRepository
{
public DataTableDTO GetAllFromBase(int start, int length, string sortColumn, string sortColumnDir, string search)
{
var dataFiltered = db.User
.AsNoTracking()
.Select(x => new { x.Id, x.FirstName, x.LastName, x.Description})
.OrderBy(sortColumn + " " + sortColumnDir)
.Where(search.Length > 0, x => x.Id.ToString().Contains(search.ToLower())
|| x.FirstName.ToLower().Contains(search.ToLower())
|| x.LastName.ToLower().Contains(search.ToLower())
|| x.Description.ToLower().Contains(search.ToLower()));
var recordsFiltered = dataFiltered.Count();
var recordsTotal = db.User.Count();
var dataToShow = dataFiltered
.Skip(start)
.Take(length)
.ToList();
var dataForTable = new DataTableDTO
{
Data = dataToShow,
RecordsTotal = recordsTotal,
RecordsFiltered = recordsFiltered
};
return dataForTable;
}
}
public static class LinqExtensions
{
public static IQueryable<T> Where<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> whereClause)
{
if (condition)
{
return query.Where(whereClause);
}
return query;
}
}
You can extract this method:
public bool SearchForMatching(string source, string serach)
{
return source.ToLower().Contains(search.ToLower());
}
It will Simplify your Where clause:
Where(x => SearchForMatching(x.Id, search)
|| SearchForMatching(x.FirstName, search)
|| SearchForMatching(x.LastName, search)
|| SearchForMatching(x.Description, search));
Another option if you don't want to search for every property one by one and you want to check if any of the class's propeties answers the search is to use reflection to iterate over the class's properties and check if there is any one that answers the condition:
public bool SearchForMatching(User user, string search)
{
return user.GetType().GetPropeties().Any(propertyInfo => propertyInfo.GetValue(user).ToString().ToLower().Contains(search.ToLower()));
}
Then use this method in the Where clause:
Where(x => SearchForMatching(x, search));
Or just combine them together:
Where(x => x.GetType().GetPropeties().Any(propertyInfo => propertyInfo.GetValue(x).ToString().ToLower().Contains(search.ToLower()));
Edit
The two options should work just fine with LINQ to Objects but it will probably not collaborate well with LINQ to Entities because it is not possible to translate neither the first option or the reflection option to SQL using LINQ to Entities.
You can load all the data to memeory using db.User.AsEnumerable() and then work with LINQ to Objects with any of these options, but it is less efficient than doing all the filtering in the database like your first query does, I suggest you despite it's readblity to keep your first query.

Can't use .ToList() with IQueryable<T> [duplicate]

I'm migrating some stuff from one mysql server to a sql server but i can't figure out how to make this code work:
using (var context = new Context())
{
...
foreach (var item in collection)
{
IQueryable<entity> pages = from p in context.pages
where p.Serial == item.Key.ToString()
select p;
foreach (var page in pages)
{
DataManager.AddPageToDocument(page, item.Value);
}
}
Console.WriteLine("Done!");
Console.Read();
}
When it enters into the second foreach (var page in pages) it throws an exception saying:
LINQ to Entities does not recognize the method 'System.String
ToString()' method, and this method cannot be translated into a store
expression.
Anyone know why this happens?
Just save the string to a temp variable and then use that in your expression:
var strItem = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == strItem
select p;
The problem arises because ToString() isn't really executed, it is turned into a MethodGroup and then parsed and translated to SQL. Since there is no ToString() equivalent, the expression fails.
Note:
Make sure you also check out Alex's answer regarding the SqlFunctions helper class that was added later. In many cases it can eliminate the need for the temporary variable.
As others have answered, this breaks because .ToString fails to translate to relevant SQL on the way into the database.
However, Microsoft provides the SqlFunctions class that is a collection of methods that can be used in situations like this.
For this case, what you are looking for here is SqlFunctions.StringConvert:
from p in context.pages
where p.Serial == SqlFunctions.StringConvert((double)item.Key.Id)
select p;
Good when the solution with temporary variables is not desirable for whatever reasons.
Similar to SqlFunctions you also have the EntityFunctions (with EF6 obsoleted by DbFunctions) that provides a different set of functions that also are data source agnostic (not limited to e.g. SQL).
The problem is that you are calling ToString in a LINQ to Entities query. That means the parser is trying to convert the ToString call into its equivalent SQL (which isn't possible...hence the exception).
All you have to do is move the ToString call to a separate line:
var keyString = item.Key.ToString();
var pages = from p in context.entities
where p.Serial == keyString
select p;
Cast table to Enumerable, then you call LINQ methods with using ToString() method inside:
var example = contex.table_name.AsEnumerable()
.Select(x => new {Date = x.date.ToString("M/d/yyyy")...)
But be careful, when you calling AsEnumerable or ToList methods because you will request all data from all entity before this method. In my case above I read all table_name rows by one request.
Had a similar problem.
Solved it by calling ToList() on the entity collection and querying the list.
If the collection is small this is an option.
IQueryable<entity> pages = context.pages.ToList().Where(p=>p.serial == item.Key.ToString())
Hope this helps.
Upgrading to Entity Framework Version 6.2.0 worked for me.
I was previously on Version 6.0.0.
Hope this helps,
Change it like this and it should work:
var key = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == key
select p;
The reason why the exception is not thrown in the line the LINQ query is declared but in the line of the foreach is the deferred execution feature, i.e. the LINQ query is not executed until you try to access the result. And this happens in the foreach and not earlier.
If you really want to type ToString inside your query, you could write an expression tree visitor that rewrites the call to ToString with a call to the appropriate StringConvert function:
using System.Linq;
using System.Data.Entity.SqlServer;
using System.Linq.Expressions;
using static System.Linq.Expressions.Expression;
using System;
namespace ToStringRewriting {
class ToStringRewriter : ExpressionVisitor {
static MethodInfo stringConvertMethodInfo = typeof(SqlFunctions).GetMethods()
.Single(x => x.Name == "StringConvert" && x.GetParameters()[0].ParameterType == typeof(decimal?));
protected override Expression VisitMethodCall(MethodCallExpression node) {
var method = node.Method;
if (method.Name=="ToString") {
if (node.Object.GetType() == typeof(string)) { return node.Object; }
node = Call(stringConvertMethodInfo, Convert(node.Object, typeof(decimal?));
}
return base.VisitMethodCall(node);
}
}
class Person {
string Name { get; set; }
long SocialSecurityNumber { get; set; }
}
class Program {
void Main() {
Expression<Func<Person, Boolean>> expr = x => x.ToString().Length > 1;
var rewriter = new ToStringRewriter();
var finalExpression = rewriter.Visit(expr);
var dcx = new MyDataContext();
var query = dcx.Persons.Where(finalExpression);
}
}
}
In MVC, assume you are searching record(s) based on your requirement or information.
It is working properly.
[HttpPost]
[ActionName("Index")]
public ActionResult SearchRecord(FormCollection formcollection)
{
EmployeeContext employeeContext = new EmployeeContext();
string searchby=formcollection["SearchBy"];
string value=formcollection["Value"];
if (formcollection["SearchBy"] == "Gender")
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Gender == value).ToList();
return View("Index", emplist);
}
else
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Name == value).ToList();
return View("Index", emplist);
}
}
I got the same error in this case:
var result = Db.SystemLog
.Where(log =>
eventTypeValues.Contains(log.EventType)
&& (
search.Contains(log.Id.ToString())
|| log.Message.Contains(search)
|| log.PayLoad.Contains(search)
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
)
)
.OrderByDescending(log => log.Id)
.Select(r => r);
After spending way too much time debugging, I figured out that error appeared in the logic expression.
The first line search.Contains(log.Id.ToString()) does work fine, but the last line that deals with a DateTime object made it fail miserably:
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
Remove the problematic line and problem solved.
I do not fully understand why, but it seems as ToString() is a LINQ expression for strings, but not for Entities. LINQ for Entities deals with database queries like SQL, and SQL has no notion of ToString(). As such, we can not throw ToString() into a .Where() clause.
But how then does the first line work? Instead of ToString(), SQL have CAST and CONVERT, so my best guess so far is that linq for entities uses that in some simple cases. DateTime objects are not always found to be so simple...
My problem was that I had a 'text' data type for this column (due to a migration from sqlite).
Solution: just change the data type to 'nvarchar()' and regenerate the table.
Then Linq accepts the string comparison.
I am working on retiring Telerik Open Access and replacing it with Entity Framework 4.0. I came across same issue that telerik:GridBoundColumn filtering stopped working.
I find out that its not working only on System.String DataTypes. So I found this thread and solved it by just using .List() at the end of my Linq query as follows:
var x = (from y in db.Tables
orderby y.ColumnId descending
select new
{
y.FileName,
y.FileSource,
y.FileType,
FileDepartment = "Claims"
}).ToList();
Just turn the LINQ to Entity query into a LINQ to Objects query (e.g. call ToArray) anytime you need to use a method call in your LINQ query.

Make a Search Method generic using LINQ

I have a method in my project that repeats over and over:
public PAC PAC_GetByCodiPac(string codiPac)
{
var sel = _gam.PAC.Where(pac => pac.CODI_PAC == codiPac);
if (sel.Count() > 0)
return sel.First();
return null;
}
The table PAC means (patient), so I have these methods for all the tables I have.
How can I make a generic method for this?
Thanks in advance.
Here is your generic method. Note, that as others pointed out FirstOrDefault is better than count and then first, so I'm using it here. But it's also possible to write the expression so that it mimics what your original code does. Please let me know if you need additional help with this.
public static T GetByCodi<T>(IQueryable<T> table, string codi, string fieldName) where T : class
{
// x
ParameterExpression parameter = Expression.Parameter(typeof(T), "x");
Expression currentExpression = parameter;
Type currentType = typeof(T);
PropertyInfo property = currentType.GetProperty(fieldName);
// x.CODI_xxx
currentExpression = Expression.Property(currentExpression, property);
// x.CODI_xxx == codi
currentExpression = Expression.Equal(currentExpression, Expression.Constant(codi));
// x => x.CODI_xxx == codi
LambdaExpression lambdaExpression = Expression.Lambda(currentExpression, parameter);
return table.FirstOrDefault((Func<T, bool>)lambdaExpression.Compile());
}
You use it like this:
PAC xxx = GetByCodi<PAC>(_gam.PAC, codiPac, "CODI_PAC");
Edit 1:
I changed the code according to the comment so that you can pass arbitrary ID field name in.
I see that what you asked is a very straight forward where query even doesn't require to have have it on a separate method.
Also you can simply enhance your query link the following:
public PAC PAC_GetByCodiPac(string codiPac)
{
return _gam.PAC.FirstOrDefault(pac => pac.CODI_PAC == codiPac);
}
FirstOrDefault will return the first item on the array, if not it will return null.
If you want a generic method that lets you specify any table and any predicate for records from that table then you can't really get any better than the built-in Where<T>(...) and (as others have already pointed out) the FirstOrDefault<T>(...) extension methods.
Your code would then look like so:
var result = _gam.PAC.Where(pac => pac.CODI_PAC == codiPac).FirstOrDefault();
// OR
var result = _gam.PAC.FirstOrDefault(pac => pac.CODI_PAC == codiPac);
The best you could get then, writing your own generic method, would be this:
public T FirstOrDefault<T>(IQueryable<T> source,
Expression<Func<T, bool>> predicate)
{
return source.Where(predicate).FirstOrDefault();
// OR
// return source.FirstOrDefault(predicate);
}
And that is really just redundant. Especially when your calling code would be actually longer using the helper method:
var result = FirstOrDefault(_gam.PAC, pac => pac.CODI_PAC == codiPac);
// versus
var result = _gam.PAC.FirstOrDefault(pac => pac.CODI_PAC == codiPac);
And even worse, your code is no longer using a fluent, composable syntax. This just makes readability and maintenance more difficult.
If you stick with using the IQueryable<T> extension methods then you can do composition like this:
var result = _gam.PAC
.Where(pac => pac.CODI_PAC == codiPac)
.Where(pac => pac.SomeOtherProperty == someOtherValue)
.FirstOrDefault();
// OR
var result = (from pac in _gam.PAC
where pac.CODI_PAC == codiPac
where pac.SomeOtherProperty == someOtherValue
select pac).FirstOrDefault();
One very important thing to note here is that the predicate parameter in the IQueryable<T>.Where<T>(...) extension method is of type Expression<Func<T, bool>>. This allows the IQueryable<T> provider to construct the native SQL (or other native provider query) at the very last moment before returning a result.
Not using Expression<Func<T, bool>> means that your query would be the equivalent of this:
var result =
_gam.PAC
.ToArray()
.Where(pac => pac.CODI_PAC == codiPac)
.FirstOrDefault();
And that would mean the query will load every record from the "PAC" table into memory before selecting the first filtered result and throwing out the rest of the results.
The bottom-line is that by making a generic helper method you are rewriting existing framework code and you open yourself to performance and maintenance issues while also reducing code readability.
I hope this helps.
I'm not sure if you are asking for this, but this method could be in a static class and method and so you'd be able to call it from everywhere.
An easy solution will be:
//a generic method
private PAC PAC_GetPAC(Func<PAC, bool> predicate)
{
return _gam.PAC.Where(predicate).FirstOrDefault();
}
public PAC PAC_GetPACById(long id)
{
return PAC_GetPAC(p => p.ID == id);
}
public PAC PAC_GetByCodiPac(string codiPac)
{
return PAC_GetPAC(p => pac.CODI_PAC == codiPac);
}

Categories

Resources