Linq to entity - dynamic query - linq

public void ApproveRowTable(string tablename, List<int> idValues)
{
foreach (var x in idValues)
{
var context = new SSPModel.sspEntities();
var genRules = (from a in context.GeneralRules
where a.ID == x
select a).SingleOrDefault();
genRules.Approved_by = GlobalClass.GlobalVar;
genRules.Approved_on = DateTime.Now;
context.SaveChanges();
}
}
In my query (from a in context.GeneralRules...) I would like to make it query base on a parameter (tablename) rather than i have to go and supply the name of the table in the query (as it is doing right now.). Any way i can get it to do that .. basic.. from a in context.TABLENAME -- TABLENAME is a parameter that is going to be passed when the function is called. Help

This will be difficult if your entity types do not all implement the same interface or derive from the same class. If they do, it's pretty simple:
// example base type, which your entities would need to implement
public interface IApprovable
{
public int ID {get; set;}
public string Approved_by {get; set;}
public DateTime Approved_on {get; set;}
}
//...
public void ApproveRowTable<T>(List<int> idValues)
where T : IApprovable
{
using(var context = new SSPModel.sspEntities())
{
var table = context.Set<T>();
var entities = table.Where(e => idValues.Contains(e.ID));
foreach(var entity in entities)
{
entity.Approved_by = GlobalClass.GlobalVar;
entity.Approved_on = DateTime.Now;
}
context.SaveChanges();
}
}
If your entity types do not implement a common base type, then you should modify them by creating empty partials which do implement it:
public partial class GeneralRule : IApprovable {}
If you cannot do that, then you can do something like the following. (I'm assuming ID is the PK, so we can use Find() rather than needing to build an expression:
public void ApproveTableRows(Type entityType, IEnumerable<int> idsToApprove)
{
using(var context = new SSPModel.sspEntities())
{
var set = context.Set(entityType);
if(set == null)
throw new ArgumentException("No DbSet found with provided name", "tableSetName");
var approveByProperty = entityType.GetProperty("Approved_by");
var approveOnProperty = entityType.GetProperty("Approved_on");
if(approveByProperty == null || approveOnProperty == null)
throw new InvalidOperationException("Entity type does not contain approval properties");
foreach (object id in idsToApprove)
{
var entityInstance = set.Find(id);
approveByProperty.SetValue(entityInstance, GlobalClass.GlobalVar);
approveOnProperty.SetValue(entityInstance, DateTime.Now);
}
context.SaveChanges();
}
}
As you can see, this is less efficient, as it issues a new query for each ID rather than getting them all at once. Also, the method accepts an entity Type rather than a string, to avoid the need to hunt down the right property by reflection. This could be improved, but really I think you should probably update your entities to implement a shared interface.

I assume you would like to have the method generic. When you are using EF all your tables are represented as objects, so you don't have to specify which table you want by name, just use a generic parameter.
I doubt that my solution is best, but it should work. But I have to warn you, reflection is slow and many times its usage is not right.
public void ApproveRowTable<T>(List<int> idValues)
{
var context = new SSPModel.sspEntities();
var table = context.GetType().GetProperties().OfType<T>().Single();
var genRules = (from a in table
where a.ID == x
select a).SingleOrDefault();
genRules.Approved_by = GlobalClass.GlobalVar;
genRules.Approved_on = DateTime.Now;
context.SaveChanges();
}

Related

How to select multiple class properties in LINQ Expression?

If I have a class like this
`
class Person
{
public string First;
public string Last;
public bool IsMarried;
public int Age;
}`
Then how can I write a LINQ Expression where I could select properties of a Person. I want to do something like this (user can enter 1..n properties)
SelectData<Person>(x=>x.First, x.Last,x.Age);
What would be the input expression of my SelectData function ?
SelectData(Expression<Func<TEntity, List<string>>> selector); ?
EDIT
In my SelectData function I want to extract property names and then generate SELECT clause of my SQL Query dynamically.
SOLUTION
Ok, so what I have done is to have my SelectData as
public IEnumerable<TEntity> SelectData(Expression<Func<TEntity, object>> expression)
{
NewExpression body = (NewExpression)expression.Body;
List<string> columns = new List<string>();
foreach(var arg in body.Arguments)
{
var exp = (MemberExpression)arg;
columns.Add(exp.Member.Name);
}
//build query
And to use it I call it like this
ccc<Person>().SelectData(x => new { x.First, x.Last, x.Age });
Hopefully it would help someone who is looking :)
Thanks,
IY
I think it would be better to use delegates instead of Reflection. Apart from the fact that delegates will be faster, the compiler will complain if you try to fetch property values that do not exist. With reflection you won't find errors until run time.
Luckily there is already something like that. it is implemented as an extension function of IEnumerable, and it is called Select (irony intended)
I think you want something like this:
I have a sequence of Persons, and I want you to create a Linq
statement that returns per Person a new object that contains the
properties First and Last.
Or:
I have a sequence of Persns and I want you to create a Linq statement
that returns per Person a new object that contains Age, IsMarried,
whether it is an adult and to make it difficult: one Property called
Name which is a combination of First and Last
The function SelectData would be something like this:
IEnumerable<TResult> SelectData<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
{
return source.Select(selector);
}
Usage:
problem 1: return per Person a new object that contains the
properties First and Last.
var result = Persons.SelectData(person => new
{
First = person.First,
Last = person.Last,
});
problem 2: return per Person a new object that contains Age, IsMarried, whether he is an adult and one Property called Name which is a combination
of First and Last
var result = Persons.SelectData(person => new
{
Age = person.Name,
IsMarried = person.IsMarried,
IsAdult = person.Age > 21,
Name = new
{
First = person.First,
Last = person.Last,
},
});
Well let's face it, your SelectData is nothing more than Enumerable.Select
You could of course create a function where you'd let the caller provide a list of properties he wants, but (1) that would limit his possibilities to design the end result and (2) it would be way more typing for him to call the function.
Instead of:
.Select(p => new
{
P1 = p.Property1,
P2 = p.Property2,
}
he would have to type something like
.SelectData(new List<Func<TSource, TResult>()
{
p => p.Property1, // first element of the property list
p -> p.Property2, // second element of the property list
}
You won't be able to name the returned properties, you won't be able to combine several properties into one:
.Select(p => p.First + p.Last)
And what would you gain by it?
Highly discouraged requirement!
You could achive similar result using Reflection and Extension Method
Model:
namespace ConsoleApplication2
{
class Person
{
public string First { get; set; }
public string Last { get; set; }
public bool IsMarried { get; set; }
public int Age { get; set; }
}
}
Service:
using System.Collections.Generic;
using System.Linq;
namespace Test
{
public static class Service
{
public static IQueryable<IQueryable<KeyValuePair<string, object>>> SelectData<T>(this IQueryable<T> queryable, string[] properties)
{
var queryResult = new List<IQueryable<KeyValuePair<string, object>>>();
foreach (T entity in queryable)
{
var entityProperties = new List<KeyValuePair<string, object>>();
foreach (string property in properties)
{
var value = typeof(T).GetProperty(property).GetValue(entity);
var entityProperty = new KeyValuePair<string, object>(property, value);
entityProperties.Add(entityProperty);
}
queryResult.Add(entityProperties.AsQueryable());
}
return queryResult.AsQueryable();
}
}
}
Usage:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Test
{
class Program
{
static void Main(string[] args)
{
var list = new List<Person>()
{
new Person()
{
Age = 18,
First = "test1",
IsMarried = false,
Last = "test2"
},
new Person()
{
Age = 40,
First = "test3",
IsMarried = true,
Last = "test4"
}
};
var queryableList = list.AsQueryable();
string[] properties = { "Age", "Last" };
var result = queryableList.SelectData(properties);
foreach (var element in result)
{
foreach (var property in element)
{
Console.WriteLine($"{property.Key}: {property.Value}");
}
}
Console.ReadKey();
}
}
}
Result:
Age: 18
Last: test2
Age: 40
Last: test4

The entity or complex type 'AdventureWorks2012Model.Product' cannot be constructed in a LINQ to Entities query

As you can see, I got this error when I built Data Gird using Kendo UI. Does anybody could point out where I'm wrong in my code below.
private IEnumerable<Product> GetSubProduct()
{
var context = new AdvenDBEntities();
var subcate = context.Products.Select(p => new Product
{
ProductID = p.ProductID,
Name = p.Name,
Color = p.Color,
ListPrice = p.ListPrice,
}).ToList();
return subcate;
}
Error:
The entity or complex type 'AdventureWorks2012Model.Product' cannot be constructed in a LINQ to Entities query.
Thank you so much for your time!
Since Product is an entity of model, you are creating new object of this entity while selecting the records, which is NOT good idea, I am NOT sure how model will handle this kind of behaviour that is why it is preventing you to do so, (I guess). Anyway you can change the code to this,
private IEnumerable<Product> GetSubProduct()
{
var context = new AdvenDBEntities();
var subcate = context.Products.ToList();
return subcate;
}
BTW your function name indicating that you are missing a Where clause.
Also you can create some custom DTO class and use it instead.
E.g.
class ProductDTO
{
public int ProductID { get; set; }
public string Name { get; set; }
public string Color { get; set; }
public decimal ListPrice { get; set; }
}
private IEnumerable<ProductDTO> GetSubProduct()
{
var context = new AdvenDBEntities();
var subcate = context.Products.Select(p => new ProductDTO
{
ProductID = p.ProductID,
Name = p.Name,
Color = p.Color,
ListPrice = p.ListPrice,
}).ToList();
return subcate;
}
The first bad smell code I can point out for you. DBContext implements IDisposable so you are responsible for calling Dispose on it. In all, but one case in here, using block
You must build query to get all the product and then extract from it.
private IEnumerable<Product> GetSubProduct()
{
using (var context = new AdvenDBEntities())
{
// Get all constructed type product and then select from it
var subcate = context.Products
.ToList()
.Select(p => new Product
{
ProductID = p.ProductID,
Name = p.Name,
Color = p.Color,
ListPrice = p.ListPrice,
});
return subcate;
}
}

How does DynamicQueryable support the Array operator?

I have been using the DynamicQueryable Linq extensions featured in Scott Guthrie's blog post.
The documentation has a table of supported operators. One of the primary operators is the following:
x[…]
Array or indexer access. Multi-dimensional arrays are not supported.
However, I cannot figure out how it can be used.
I didn't expect any of the following to work and in fact they don't.
var ctx = new MyDbContext();
var parameters = new Object[] { new int[] { 1, 2, 3 } };
var qry = ctx.Set<User>().Where<User>("it.Id in #0", parameters);
var qry = ctx.Set<User>().Where<User>("it.Id.In(#0)", parameters);
var qry = ctx.Set<User>().Where<User>("it.Id = #0", parameters);
var qry = ctx.Set<User>().Where<User>("#0.Contains(it.Id)", parameters);
It is basically an In query, but I am not sure how to express it.
This is perhaps a misunderstanding. Meant is that it is possible to query for collection elements at a specific index position. For example:
public class Order
{
public List<OrderDetail> OrderDetails { get; set; }
}
public class OrderDetail
{
public string Description { get; set; }
}
Then you can query for all orders which have the Detail description "Bicycle" in the third OrderDetail by:
string parameter = "Bicycle";
var qry = ctx.Set<Order>().Where<Order>("it.OrderDetails[2].Description == #0",
parameter);
I think for your purpose you need to build up an "OR" chain "it.Id == 1 or it.Id == 2 or it.Id == 3" (or build this query string dynamically in a loop) without parameters in the Where method.

Why predicate isn't filtering when building it via reflection

I'm building a rather large filter based on an SearchObject that has 50+ fields that can be searched.
Rather than building my where clause for each one of these individually I thought I'd use some slight of hand and try building custom attribute suppling the necessary information and then using reflection to build out each of my predicate statements (Using LinqKit btw). Trouble is, that the code finds the appropriate values in the reflection code and successfully builds a predicate for the property, but the "where" doesn't seem to actually generate and my query always returns 0 records.
The attribute is simple:
[AttributeUsage(AttributeTargets.Property, AllowMultiple=true)]
public class FilterAttribute: Attribute
{
public FilterType FilterType { get; set; } //enum{ Object, Database}
public string FilterPath { get; set; }
//var predicate = PredicateBuilder.False<Metadata>();
}
And this is my method that builds out the query:
public List<ETracker.Objects.Item> Search(Search SearchObject, int Page, int PageSize)
{
var predicate = PredicateBuilder.False<ETracker.Objects.Item>();
Type t = typeof(Search);
IEnumerable<PropertyInfo> pi = t.GetProperties();
string title = string.Empty;
foreach (var property in pi)
{
if (Attribute.IsDefined(property, typeof(FilterAttribute)))
{
var attrs = property.GetCustomAttributes(typeof(FilterAttribute),true);
var value = property.GetValue(SearchObject, null);
if (property.Name == "Title")
title = (string)value;
predicate.Or(a => GetPropertyVal(a, ((FilterAttribute)attrs[0]).FilterPath) == value);
}
}
var res = dataContext.GetAllItems().Take(1000)
.Where(a => SearchObject.Subcategories.Select(b => b.ID).ToArray().Contains(a.SubCategory.ID))
.Where(predicate);
return res.ToList();
}
The SearchObject is quite simple:
public class Search
{
public List<Item> Items { get; set; }
[Filter(FilterType = FilterType.Object, FilterPath = "Title")]
public string Title { get; set; }
...
}
Any suggestions will be greatly appreciated. I may well be going way the wrong direction and will take no offense if someone has a better alternative (or at least one that works)
You're not assigning your predicate anywhere. Change the line to this:
predicate = predicate.Or(a => GetPropertyVal(a, ((FilterAttribute)attrs[0]).FilterPath) == value);

Using structuremap getinstance in linq statement with initializer values

I'm not sure how to ask this, but I want to create a new object in a linq statement, and initialize it's values. Since I have to use StructureMap's GetInstance method to get an instance of the object, I don't think this is possible. Am I wrong? And before we get off-topic, I realize I could probably create a constructor that takes the values I want to use, but I want to know if there's any way that I can do it with initializers and structuremap. I'm really weak on structuremap, and for all I know, there may be a simple way I'm just missing...
Thanks for any thoughts.
//basically, I'm creating a new object when I don't have an existing one for the groupid
//conceptually, this is adding users to the selected groups if they don't already belong
from g in GroupIDs where !OriginalGroups.Exists(ug => ug.SecurityGroupID == g) select StructureMap.ObjectFactory.GetInstance() {//initialize values of usertosecuritygroup};
//this is how I would do it if I wasn't relying on DI.
from g in GroupIDs where !OriginalGroups.Exists(ug => ug.SecurityGroupID == g) select new UserToGroup {UserID = UserID, GroupID = g};
If you start using method chains rather than LINQ expressions you could do something like this:
[TestFixture]
public class so_1
{
public class UserToGroup
{
public int UserId { get; set; }
public int GroupId { get; set; }
}
[Test]
public void TEST()
{
var container = new Container();
var groupIds = new int[] { 1,2,3,4,5};
var originaGroups = new int[] { 4, 5 };
var result = groupIds.Intersect(originaGroups).Select(group =>
{
var myClass = container.GetInstance<UserToGroup>();
myClass.GroupId = group;
return myClass;
});
result.First().GroupId.ShouldEqual(4);
result.Skip(1).First().GroupId.ShouldEqual(5);
}
}

Resources