Dynamic LINQ query to get Field value from Database - c#

is it possible?
Public String Get_Filed_By_Id(string table_Name,String Field_Name,string PK_val)
{
string strRes="";
using(mydbcontext db=new mydbcontext())
{
var x=db.table_Name.Where(p=>p.Id=PK_val).FirstOrDefault().Field_Name;
strRes=Convert.Tostring(x);
}
return strRes;
}
OR
var x=(from o in db.table_Name where o.Id=PK_val select o.Field_Name).FirstOrDefault();
Here, i'm passing Table_Name,Column_Name and the Condition value(PK_val) to Get the Column_Name from Table_Name within a Certain Condition(Id=Pk_val).
Is it possible??

Is it possible??
Yes, it is.
First, some helpers:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace YourNamespace
{
internal static class DbHelpers
{
public static object GetColumnById(this object dbContext, string tableName, string columnName, object id)
{
var table = (IQueryable)dbContext.GetType().GetProperty(tableName).GetValue(dbContext, null);
var row = Expression.Parameter(table.ElementType, "row");
var filter = Expression.Lambda(Expression.Equal(Expression.Property(row, "Id"), Expression.Constant(id)), row);
var column = Expression.Property(row, columnName);
var selector = Expression.Lambda(column, row);
var query = Call(Where.MakeGenericMethod(row.Type), table, filter);
query = Call(Select.MakeGenericMethod(row.Type, column.Type), query, selector);
var value = Call(FirstOrDefault.MakeGenericMethod(column.Type), query);
return value;
}
private static readonly MethodInfo Select = GetGenericMethodDefinition<
Func<IQueryable<object>, Expression<Func<object, object>>, IQueryable<object>>>((source, selector) =>
Queryable.Select(source, selector));
private static readonly MethodInfo Where = GetGenericMethodDefinition<
Func<IQueryable<object>, Expression<Func<object, bool>>, object>>((source, predicate) =>
Queryable.Where(source, predicate));
private static readonly MethodInfo FirstOrDefault = GetGenericMethodDefinition<
Func<IQueryable<object>, object>>(source =>
Queryable.FirstOrDefault(source));
private static MethodInfo GetGenericMethodDefinition<TDelegate>(Expression<TDelegate> e)
{
return ((MethodCallExpression)e.Body).Method.GetGenericMethodDefinition();
}
private static object Call(MethodInfo method, params object[] parameters)
{
return method.Invoke(null, parameters);
}
}
}
and now your function:
public string Get_Field_By_Id(string table_Name, string field_Name, string PK_val)
{
using (var db = new mydbcontext())
return Convert.ToString(db.GetColumnById(table_Name, field_Name, PK_val));
}

It is not really possible with EntityFramework actually(as far as I know). If you only needed the field by its name, then you could have used #Den's proposed solution. But you want to specify the table name too as a parameter. So I suggest you to use standard Sql Connector api, and build the query string with the parameters you provide.
Check this link for usage of standard sql connector api.

I had this question too ,I know this is not exactly what you want and you need write more code but it's much cleaner than those you want to write.
Using repository pattern
For every table you should have a model class and Repository class.
Consider this code(this code from one of my project)
This is my comment table(this can be anything with or without navigation property)
public sealed class Comment
{
public string CommentText { get; set; }
public DateTime PostDate { get; set; }
public int PostId { get; set; }
public int? PageId { get; set; }
public Page Page { get; set; }
public User User { get; set; }
public string UserId { get; set; }
public int? ParentId { get; set; }
public Comment[] ChildComments { get; set; }
}
RepositoryComment
public sealed class CommentRepository : BaseRepository<Comment>
{
public CommentRepository(BabySitterContext context)
: base(context)
{
}
}
and a base class that you send your query with table name(here model) and field(you can extend clas for more functionality)
public class BaseRepository<T> where T : class
{
protected BabySitterContext Context;
private readonly PluralizationService _pluralizer = PluralizationService.CreateService(CultureInfo.GetCultureInfo("en"));
public BaseRepository(BabySitterContext context)
{
this.Context = context;
}
public bool Add(T t)
{
Context.Set<T>().Add(t);
Context.SaveChanges();
return true;
}
public bool Update(T t)
{
var entityName = GetEntityName<T>();
object originalItem;
var key = ((IObjectContextAdapter)Context).ObjectContext.CreateEntityKey(entityName, t);
if (((IObjectContextAdapter)Context).ObjectContext.TryGetObjectByKey(key, out originalItem))
{
((IObjectContextAdapter)Context).ObjectContext.ApplyCurrentValues(key.EntitySetName, t);
}
Context.SaveChanges();
return true;
}
public void Attach(T t)
{
if (t == null)
{
throw new ArgumentNullException("t");
}
Context.Set<T>().Attach(t);
Context.SaveChanges();
}
public void Remove(T t)
{
if (t == null)
{
throw new ArgumentNullException("t");
}
Context.Set<T>().Remove(t);
Context.SaveChanges();
}
public IEnumerable<T> Get(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "")
{
IQueryable<T> query = Context.Set<T>();
if (filter != null)
{
query = query.Where(filter.Expand());
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
private string GetEntityName<TEntity>() where TEntity : class
{
return string.Format("{0}.{1}", ((IObjectContextAdapter)Context).ObjectContext.DefaultContainerName, _pluralizer.Pluralize(typeof(TEntity).Name));
}
public virtual IEnumerable<T> GetByBusinessKey(T entity)
{
return null;
}
}
For any other table just make model class and reposiotry then inherite from base class
Using code
var context = new BabySitterContext();
var _commentRepository = new CommentRepository(context);
var comment = _commentRepository.Get(x => x.PostId == id).FirstOrDefault();

No, but in this way
Public String Get_Filed_By_Id(string table_Name,String Field_Name,string PK_val)
{
string strRes="";
using(mydbcontext db=new mydbcontext())
{
var x=db.table_Name.Where(p=>p.Id=PK_val).Select(b=>b.Field_Name).FirstOrDefault();
strRes=Convert.Tostring(x);
}
return strRes;
}

Related

Accessing nested properties and collections using Expression

I have the following classes:
public class Person
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("age")]
public int Age { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
[JsonProperty("profession")]
public Profession Profession { get; set; }
[JsonProperty("hobbies")]
public List<string> Hobbies { get; set; }
}
public class Profession
{
[JsonProperty("name")]
public string ProfessionName { get; set; }
[JsonProperty("industry")]
public string Industry { get; set; }
[JsonProperty("salary")]
public string AverageSalary { get; set; }
[JsonProperty("activities")]
public List<WorkActivity> WorkActivities { get; set; }
}
public class WorkActivity
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("rooms")]
public List<string> Rooms { get; set; }
}
public class PropertiesVisitor : ExpressionVisitor
{
private readonly Expression param;
public List<string> Names { get; } = new List<string>();
public PropertiesVisitor(Expression parameter)
{
param = parameter;
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression == param)
{
Names.Add(node.Member.GetCustomAttribute<JsonPropertyAttribute>().PropertyName);
}
return base.VisitMember(node);
}
}
I call the methods in my app like this:
private static List<string> FindPropertyNames<T>(Expression<Func<T, object>> e)
{
var visitor = new PropertiesVisitor(e.Parameters[0]);
visitor.Visit(e);
return visitor.Names;
}
public static void Main(string[] args)
{
var names = FindPropertyNames<Person>(x => new { x.Age, x.Country, x.Profession.AverageSalary, x.Hobbies });
Console.WriteLine(string.Join(" ", names));
}
It works fine, except for one minor detailed - it doesn't check for nested properties. The output is the following: age country profession hobbies. I want it to be age country profession.salary hobbies.
I've been trying to fix the issue using a different approach, but I am unable to do so fully. I've tried the following:
public static MemberExpression GetMemberExpression(Expression e)
{
if (e is MemberExpression)
{
return (MemberExpression)e;
}
else if (e is LambdaExpression)
{
var le = e as LambdaExpression;
if (le.Body is MemberExpression)
{
return (MemberExpression)le.Body;
}
else if (le.Body is UnaryExpression)
{
return (MemberExpression)((UnaryExpression)le.Body).Operand;
}
}
return null;
}
public static string GetPropertyPath<T>(Expression<Func<T, object>> expr)
{
var path = new StringBuilder();
MemberExpression me = GetMemberExpression(expr);
do
{
if (path.Length > 0)
{
path.Insert(0, ".");
}
path.Insert(0, me.Member.GetCustomAttribute<JsonPropertyAttribute>().PropertyName);
me = GetMemberExpression(me.Expression);
}
while (me != null);
return path.ToString();
}
It kind of does the job - but it can't take more than one property.
I call it like this:
var x = GetPropertyPath<Person>(p => p.Profession.AverageSalary);
Console.WriteLine(x);
I want to be able to send multiple properties, as in the first version. Also, I am unsure how to pass the following person.Profession.WorkActivities.Rooms as a parameter, because it is a list. I want to get profession.activities.rooms as output for it.
I would write a method that takes an Expression and returns all the members in the chain:
public static IEnumerable<MemberExpression> MemberClauses(this Expression expr) {
if (expr is not MemberExpression mexpr) {
yield break;
}
foreach (var item in MemberClauses(mexpr.Expression)) {
yield return item;
}
yield return mexpr;
}
Then, you can unwrap an entire method chain and LINQ to get the JSON property names:
public class PropertiesVisitor : ExpressionVisitor {
private readonly Expression param;
public List<string> Names { get; } = new List<string>();
public PropertiesVisitor(Expression parameter) => param = parameter;
[return: NotNullIfNotNull("node")]
public override Expression Visit(Expression node) {
var chain = node.MemberClauses().ToList();
if (chain.Any() && chain.First().Expression == param) {
var name = string.Join(".", chain.Select(
mexpr => mexpr.Member.GetCustomAttribute<JsonPropertyAttribute>().PropertyName
));
Names.Add(name);
return node;
} else {
return base.Visit(node);
}
}
}
and you can then get the property path as follows:
public static class Functions {
public static List<string> GetPropertyPath<T>(Expression<Func<Person, T>> expr) {
var visitor = new PropertiesVisitor(expr.Parameters[0]);
visitor.Visit(expr);
return visitor.Names;
}
}
var names = Functions.GetPropertyPath(p => new { p.Age, p.Country, p.Profession.AverageSalary, p.Hobbies });
foreach (var name in names) {
Console.WriteLine(name);
}

How to fill some properties in model after evaluation of IQueryable in OData?

I'm using OData with Entity Framework. Let's assume that I have following models and controller method:
public class Model1
{
public int Id { get; set; }
public int Field2 { get; set; }
public int FieldFromOtherService { get; set; }
public Model2 Model2 { get; set; } // Navigation Property
public int Model2Id { get; set; }
}
public class Model2
{
public int Id { get; set; }
public int Field { get; set; }
}
[HttpGet, EnableQuery]
public IQueryable<Model1> Get()
{
return modelRepository.List();
}
Model1 has property FieldFromOtherService that is not taken from DB - it is retrieved from other service. I need a way to fill this property after applying OData top, skip, expand and select clause.
Is there a way to accomplish that? I've tried to make a wrapper to IQueryable and call action after evaluation but it crash when query is more complicated.
Finally, I manage to accomplish my goals with #zaitsman suggestion. It was harder then I thought because OData adds wrappers that are not accessible (classes SelectAllAndExpand, SelectAll, SelectSomeAndInheritance, SelectSome). When expand is used, it is necessary to extract DTO from the wrapper. My code looks more or less like this:
[HttpGet]
public IHttpActionResult Get(ODataQueryOptions<Model1> options)
{
var result = modelRepository.List();
Action<ICollection<Model1>> postAction = collection => { Console.WriteLine("Post Action"); };
return ApplyOdataOptionsAndCallPostAction(result, options, postAction);
}
private IHttpActionResult ApplyOdataOptionsAndCallPostAction<T>(
IQueryable<T> baseQuery,
ODataQueryOptions<T> options,
Action<ICollection<T>> postAction)
where T : class
{
var queryable = options.ApplyTo(baseQuery);
var itemType = queryable.GetType().GetGenericArguments().First();
var evaluatedQuery = ToTypedList(queryable, itemType);
var dtos = ExtractAllDtoObjects<T>(evaluatedQuery).ToList();
postAction(dtos)
return Ok(evaluatedQuery, evaluatedQuery.GetType());
}
private static IList ToTypedList(IEnumerable self, Type innerType)
{
var methodInfo = typeof(Enumerable).GetMethod(nameof(Enumerable.ToList));
var genericMethod = methodInfo.MakeGenericMethod(innerType);
return genericMethod.Invoke(null, new object[]
{
self
}) as IList;
}
private IEnumerable<T> ExtractAllDtoObjects<T>(IEnumerable enumerable)
where T : class
{
foreach (var item in enumerable)
{
if (item is T typetItem)
{
yield return typetItem;
}
else
{
var result = TryExtractTFromWrapper<T>(item);
if (result != null)
{
yield return result;
}
}
}
}
private static T TryExtractTFromWrapper<T>(object item)
where T : class
{
if (item is ISelectExpandWrapper wrapper)
{
var property = item.GetType().GetProperty("Instance");
var instance = property.GetValue(item);
if (instance is T val)
{
return val;
}
}
return null;
}
private IHttpActionResult Ok(object content, Type type)
{
var resultType = typeof(OkNegotiatedContentResult<>).MakeGenericType(type);
return Activator.CreateInstance(resultType, content, this) as IHttpActionResult;
}

Convert Entity with navigation property to DTO using IQueryable

Suppose I have following entities and dtos
public class Country
{
public List<NameLocalized> NamesLocalized;
public CountryData Data;
}
public class NameLocalized
{
public string Locale;
public string Value;
}
public class CountryData
{
public int Population;
}
public class CountryDto
{
public String Name;
public CountryDataDto Data;
}
public class CountryDataDto
{
public int Population;
}
I need to convert Country to CountryDto (and ideally, I want to make a single query to the database). I have received few suggestions in my other questions on Stackoverflow, and can now accomplish the task, but only partially. I am stuck at how to convert navigation property (CountryData in this case). I was suggested to use LINQKit for this, but have no idea how to implement it. Here is my code which populates only Name property but not Data navigation property.
public static async Task<List<CountryDto>> ToDtosAsync(this IQueryable<Country> source, string locale)
{
if(source == null)
{
return null;
}
var result = await source
.Select(src => new CountryDto
{
Name = src.NamesLocalized.FirstOrDefault(n => n.Locale == locale).Name
})
.ToListAsync();
return result;
}
This answer gave me the hint for my solution. You need to use LINQKit and build Expression to convert the navigation property.
public static Expression<Func<CountryData, CountryDataDto>> ConverterExpression = cd => new CountryDataDto
{
Population = cd.Population
};
public static async Task<List<CountryDto>> ToDtosAsync(this IQueryable<Country> source, string locale)
{
if(source == null)
{
return null;
}
var result = await source
.AsExpandable
.Select(src => new CountryDto
{
Name = src.NamesLocalized.FirstOrDefault(n => n.Locale == locale).Name
Data = ConverterExpression.Invoke(src.Data)
})
.ToListAsync();
return result;
}

How do I merge (or dynamically build and combine) lambda expressions in C#?

I'm trying to dynamically combin lambda expressions. The code below will explain what I want. This is NOT a case of combining a=>b and b=>c to a=>c. Instead, I want to prevent code duplication by reusing conversion-expressions:
class User // convert from...
{
public string FirstName {get;set;}
public string LastName {get;set;}
}
class Person // convert to...
{
public string Name
}
public class UserTransaction
{
User FromUser {get;set;}
User ToUser {get;set;}
decimal Amount {get;set;}
}
public class PersonTransaction
{
Person FromPerson {get;set;}
Person ToPerson {get;set;}
bool IsPositive;
}
Expression<Func<User, Person>> ToPerson = u => new Person {Name = u.FirstName + " " + u.LastName};
Expression<Func<UserTransaction, PersonTransaction>> PersonTransaction = ut => new PersonTransaction {
FromPerson = FromUser.Compile()(ut.FromUser), // Actually, I do not want to compile
ToPerson = ToUser.Compile()(ut.FromUser), // (or double code)
IsPositive = ut.Amount > 0
}
In the example above, I already have an expression to convert a user to a person. I do not want to duplicate this code or compile it. I've tried using stripping out the "compile"-calls by manually editing the expression tree. I did not succeed. Has anybody tried something similar and succeed?
You can do some voodoo with ExpressionVisitor to rewrite your existing code to be fully inline; this:
detects the invoke
locates the Compile
resolves the originating lambda
swaps in all the parameter values directly
rebuilds the expression tree accordingly
Have fun!
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;
public class User // convert from...
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Person // convert to...
{
public string Name { get; set; }
}
public class UserTransaction
{
public User FromUser { get; set; }
public User ToUser { get; set; }
public decimal Amount { get; set; }
}
public class PersonTransaction
{
public Person FromPerson { get; set; }
public Person ToPerson { get; set; }
public bool IsPositive { get; set; }
}
static class Program
{
static void Main()
{
Expression<Func<User, Person>> ToPerson = u => new Person { Name = u.FirstName + " " + u.LastName };
Expression<Func<UserTransaction, PersonTransaction>> PersonTransaction = ut => new PersonTransaction
{
FromPerson = ToPerson.Compile()(ut.FromUser), // Actually, I do not want to compile
ToPerson = ToPerson.Compile()(ut.ToUser), // (or double code)
IsPositive = ut.Amount > 0
};
var visitor = new RemoveCompilationsExpressionVisitor();
var inlined = (Expression<Func<UserTransaction, PersonTransaction>>)visitor.Visit(PersonTransaction);
}
class ParameterSwapExpressionVisitor :ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, Expression> swaps;
public ParameterSwapExpressionVisitor(Dictionary<ParameterExpression, Expression> swaps)
{
this.swaps = swaps;
}
protected override Expression VisitParameter(ParameterExpression node)
{
Expression result;
return swaps.TryGetValue(node, out result) ? result : base.VisitParameter(node);
}
}
class RemoveCompilationsExpressionVisitor : ExpressionVisitor
{
protected override Expression VisitInvocation(InvocationExpression node)
{
var lambda = TryGetInnerLambda(node.Expression);
if(lambda != null)
{
// this would be a partial solution, but we want to go further!
// return Expression.Invoke(lambda, node.Arguments);
var swaps = new Dictionary<ParameterExpression, Expression>();
for(int i = 0; i < lambda.Parameters.Count; i++)
{
swaps.Add(lambda.Parameters[i], node.Arguments[i]);
}
var visitor = new ParameterSwapExpressionVisitor(swaps);
return visitor.Visit(lambda.Body);
}
return base.VisitInvocation(node);
}
LambdaExpression TryGetInnerLambda(Expression node)
{
try
{
if(node.NodeType == ExpressionType.Call)
{
var mce = (MethodCallExpression)node;
var method = mce.Method;
if (method.Name == "Compile" && method.DeclaringType.IsGenericType && method.DeclaringType.GetGenericTypeDefinition()
== typeof(Expression<>))
{
object target;
if (TryGetLiteral(mce.Object, out target))
{
return (LambdaExpression)target;
}
}
}
}
catch (Exception ex)
{
/* best effort only */
Debug.WriteLine(ex);
}
return null;
}
static bool TryGetLiteral(Expression node, out object value)
{
value = null;
if (node == null) return false;
switch(node.NodeType)
{
case ExpressionType.Constant:
value = ((ConstantExpression)node).Value;
return true;
case ExpressionType.MemberAccess:
var me = (MemberExpression)node;
object target;
if (TryGetLiteral(me.Expression, out target))
{
switch (me.Member.MemberType)
{
case System.Reflection.MemberTypes.Field:
value = ((FieldInfo)me.Member).GetValue(target);
return true;
case MemberTypes.Property:
value = ((PropertyInfo)me.Member).GetValue(target, null);
return true;
}
}
break;
}
return false;
}
}
}

How to get list for a property of a class from its collection

I have scenario where I need to get the collection of values of a property of class.
public class Person
{
public string Name { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class PersonCollection : List<Person>
{
public object[] GetValues(string propertyName)
{
// best way to implement this method?
return null;
}
}
I want to avoid the much iterations. Any idea will help.
A bit of Linq magic:
public object[] GetValues(Expression<Func<Person, object>> exp)
{
var function = exp.Compile();
return this.Select(function).ToArray();
}
Usage:
// assuming coll in a PersonCollection
var names = coll.GetValues(p => p.Name);
A simple solution would be one that uses LINQ's Select method:
using System;
using System.Collections.Generic;
using System.Linq;
public class Person
{
public string Name { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class PersonCollection : List<Person>
{
public object[] GetValues(string propertyName)
{
if (propertyName == "Name")
{
return this.Select(p => p.Name).ToArray();
}
else if (propertyName == "Property1")
{
return this.Select(p => p.Property1).ToArray();
}
else if (propertyName == "Property2")
{
return this.Select(p => p.Property1).ToArray();
}
// best way to implement this method?
return null;
}
}
You can also use expression trees to allow for a type-safe accessor lambda to be used as an argument:
public object[] GetValues(Expression<Func<Person, object>> propertyNameExpression)
{
var compiledPropertyNameExpression = propertyNameExpression.Compile();
if (propertyNameExpression.Body.NodeType == ExpressionType.MemberAccess)
{
return this.Select(compiledPropertyNameExpression).ToArray();
}
throw new InvalidOperationException("Invalid lambda specified. The lambda should select a property.");
}
You can then use this as follows:
var personNames = personCollection.GetValues(p => p.Name)
A simple idea without using reflection would be like this:
public partial class PersonCollection: List<Person> {
public object[] GetValues(String propertyName) {
return (
from it in this
let x=
"Name"==propertyName
?it.Name
:"Property1"==propertyName
?it.Property1
:"Property2"==propertyName
?it.Property2
:default(object)
where null!=x
select x).ToArray();
}
}
But I'd rather return an IEnumerable for not to enumerate eagerly:
public partial class PersonCollection: List<Person> {
public IEnumerable GetValues(String propertyName) {
return
from it in this
let x=
"Name"==propertyName
?it.Name
:"Property1"==propertyName
?it.Property1
:"Property2"==propertyName
?it.Property2
:default(object)
where null!=x
select x;
}
}
use Reflection
Person P = new Person();
object obj = p.GetType().GetProperty(propertyName).GetValue(p, null);
try this,
public object[] GetValues(string propertyName)
{
List<object> result = new List<object>();
PropertyInfo propertyInfo = typeof(Person).GetProperty(propertyName);
this.ForEach(person => result.Add(propertyInfo.GetValue(person)));
return result.ToArray();
}
Here's a working program
public class Person
{
public string Name { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class PersonCollection : List<Person>
{
public object[] GetValues(string propertyName)
{
var result = new List<object>();
foreach (Person item in this)
{
result.Add(item.GetType().GetProperty(propertyName).GetValue(item));
}
return result.ToArray();
}
}
class Program
{
static void Main(string[] args)
{
var collection = new PersonCollection();
collection.Add(new Person(){Name = "George", Property1 = "aaa", Property2 = "bbbb"});
collection.Add(new Person(){Name = "Peter", Property1 = "ccc", Property2 = "dddd"});
var objects = collection.GetValues("Property1");
foreach (object item in objects)
{
Console.WriteLine(item.ToString());
}
Console.Read();
}
}
If you are working with entity framework,
you can use this.GetAll();

Categories

Resources