Dynamic linq enums filtering error - c#

I trying to use dynamic linq for runtime datagrid filtering with DataGridFiltering project.but i have a problem with enums.
for example, i have a class which contain an enum property like this :
public class Student
{
public Student(int id,string name,StudentType type)
{
Id = id;
Name = name;
Type = type;
}
public int Id { get; set; }
public string Name { get; set; }
public StudentType Type { get; set; }
}
and StudentType enum is :
public enum StudentType : byte
{
Normal=0,
Good
};
i create a controller class for work with list of students.
in my controller i have a method to find students by type.
this is FindByType Method:
public IList<Student> FindByType(string type)
{
return _students.AsQueryable().Where("Type.ToString().StartWith(#0)",type).ToList();
}
when i call FindByType Method i get this error in ParseMemberAccess method of dynamic linq:
Methods on type 'Enum' are not accessible

I think the problem is that the dynamic linq library you are using does not support any Enum methods, such as Enum.Equals(otherVal), or Enum.ToString(). One way to get around this, if you have to use dynamic-linq is :
public IList<Student> FindByType(StudentType type)
{
return _students.AsQueryable().Where("Type = (#0)", type).ToList();
}
However, if you are able to use standard linq, and you really want to pass in a string for some reason, something like this is much cleaner:
public IList<Student> FindByType(string type)
{
return _students.Where(s => s.Type.ToString().StartsWith(type)).ToList();
}
Edit :
If you need the ability to search using StartsWith and you are not allowed to use the standard linq query above, here is something that will give the same result with alot more code
public IList<Student> FindByType(string type)
{
//Replace e.StartsWith with whatever method you wish to filter by
var studentTypeNames =typeof(StudentType).GetEnumNames().Where(e => e.StartsWith(type)).ToList();
var students = new List<Student>();
foreach (var studentTypeName in studentTypeNames)
{
StudentType studentType;
Enum.TryParse(studentTypeName, true, out studentType);
students.AddRange(_students.AsQueryable().Where("Type = (#0)", studentType).ToList());
}
return students;
}

In Dynamic Linq you cann't call methods from class that not in predefined classes array, for workaround you may add property in Student class like this:
public string StudentTypeString {get {return Type.ToString(); } }
and use next query
public IList<Student> FindByType(string type)
{
return _students.AsQueryable().Where("StudentTypeString.StartWith(#0)",type).ToList();
}

Related

How to use LINQ to query a [key] property using generic types?

Let's say I have a class:
public class Customer
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
And now I want to create a generic Get() method that might query Customer or any one of several other classes that also have a [key] field defined.
public T Get<T>(int id)
{
string json = DoSomething(); // <-- making it easy for this post
List<T> items = JsonConvert.DeserializeObject<List<T>>(json);
return items.FirstOrDefault(i => i. ????? = id);
}
I'm not sure how to use Linq to generically specify the [key] field.
Thanks!
Hope this helps:
public interface IBase
{
int Id { get; }
}
public class Customer : IBase
{
public string Name { get; set; }
public int Id { get ; set ; }
}
public T Get<T>(int id) where T : IBase
{
string json = DoSomething(); // <-- making it easy for this post
List<T> items = JsonConvert.DeserializeObject<List<T>>(json);
return items.FirstOrDefault(i => i.Id == id);
}
Just implement the interface IBase in all other classes.
For what is worth I think using contracts is a better way to solve this. But in case you or someone else actually need to check for the attribute here's the answer:
public static T Get<T>(int id)
{
string json = DoSomething(); // <-- making it easy for this post
List<T> items = JsonConvert.DeserializeObject<List<T>>(json);
return items.FirstOrDefault(
item => (int)item.GetType()
.GetProperties()
.FirstOrDefault(
p => Attribute.IsDefined(p, typeof(KeyAttribute))
).GetValue(item) == id
);
}
As far a this part of your question:
I'm not sure how to use Linq to generically specify the [key] field.
The attribute is KeyAttribute you can know that by navigating to the definition (pressing F12 if you're using VS or checking the docs in case your editor doesn't support this feature.
Things to consider:
this is using Reflection reasonably heavily, so it will never have the best performance. That being said you can cache the result from GetProperties() somewhere for faster lookups.
It's hardcoding the cast to int but it appears that's what you're after.
If the collection is null it'll throw an exception.

NHibernate one-to-many removing related records

I have three tables with the following structure:
Todo
Id
Name
User
Id
Name
TodoUser
Id
TodoId
UserId
Status
For Todo.ResponsibleUsers I have created the following mapping:
private IList<TodoUser> responsibleUsers = new List<TodoUser>();
[Bag(0, Name = "ResponsibleUsers", Cascade = CascadeStyle.AllDeleteOrphan, Table = "TodoUser", Inverse = true)]
[Key(1, Column = "TodoId")]
[OntToMany(2, ClassType = typeof(TodoUser))]
public virtual IList<TodoUser> ResponsibleUsers {
get { return responsibleUser; }
set { responsibleUsers = (IList<TodoUser>)value; }
}
Does the property ResponsibleUsers have to be of type IList<TodoUser> or can it also be of type List<TodoUser>?
I would like to do something like todo.ResponsibleUsers.RemoveAll(itemsToRemove); which is not possible on an IList<TodoUser>
To answer the question:
Does the property ResponsibleUsers have to be of type IList<TodoUser> or can it also be of type List<TodoUser>?
Check the doc:
Chapter 6. Collection Mapping
6.1. Persistent Collections
NHibernate requires that persistent collection-valued fields be
declared as a generic interface type, for example:
public class Product
{
private string serialNumber;
private ISet<Part> parts = new HashSet<Part>();
public ISet<Part> Parts
{
get { return parts; }
set { parts = value; }
}
public string SerialNumber
{
get { return serialNumber; }
set { serialNumber = value; }
}
}
The actual interface might be
System.Collections.Generic.ICollection<T>,
System.Collections.Generic.IList<T>,
System.Collections.Generic.IDictionary<K, V>,
System.Collections.Generic.ISet<T>
So, yes, we must use interface.
To solve the issue with
I would like to do something like todo.ResponsibleUsers.RemoveAll(itemsToRemove);
We can implement some custom extension method as
public static class Ext
{
public static void RemoveAll(this IList<T> list, IEnumerable<T> toRemove)
{
... // remove items matching toRemove from the list
}

generic class for linq / lambda expression

C# Entity framework 4.0
I have a database with 10's of table with 2 common columns 'id' and 'modstamp'
to access modstamp in a table I have a function
protected internal override string GetModStampinChild(int sid)
{
DBContext sq = new DBContext();
return sq.xxxx.Where(s => s.id == sid)
.Select(s => s.modstamp).SingleOrDefault().ToModStampString();
}
where xxxx change for every table.
I am presently overriding this function for every table.
Is there a way to use some kind of generic "class" which I could use where "xxxx" would be any table?
First, you would need to have all of your Entities implement either an interface or an abstract class that contains both the ID and ModStamp properties in it, let's call it Stampable:
public abstract class Stampable
{
[Key]
public int ID { get; set; }
[Required]
public string ModStamp { get; set; }
}
At that point, all you need to do for your method is to have it implement generic typing:
protected internal override string GetModStampInChild<T>(int sid) where T : Stampable
{
using (var sq = new DbContext())
{
return sq.Set<T>.Where(s => s.id == sid)
.Select(s => s.modstamp)
.SingleOrDefault()
.ToModStampString();
}
}
If I understand you correctly, you need a property Set<T> of DbContext class:
First, create base class of all your entity classes with id and modstamp properties. Then:
protected internal override string GetModStampInChild<T>(int sid) where T : BaseEntity
{
using (var sq = new DbContext())
{
return sq.Set<T>.Where(s => s.id == sid)
.Select(s => s.modstamp)
.SingleOrDefault()
.ToModStampString();
}
}
But you must use code-first paradigm for this method.
Another option would be add a new Property to your entity class via the partial class feature of c#.
So the entity definition generated might look like this, note I have no idea what the actual DataType of your ModStamp column is:
public partial class Company
{
public int Id { get; set; }
public byte[] ModStamp { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }
}
Note the ModStamp column that you want to convert.
Then add to the Partial.cs file that EF creates code like this, note I have no idea what you actually want to do with the ModStamp value:
public static class ModConverter
{
public static string ToModStampString(byte[] modStamp)
{
return BitConverter.ToString(modStamp);
}
}
public partial class Company
{
public string ModStampString
{
get
{
return ModConverter.ToModStampString(this.ModStamp);
}
}
}
You would then have to manually add a new ModStampString Get Property for every Entity with a ModStamp Column like I did for the Company Entity.
Here is a solution that uses the Set method on the DbContext and expression trees to dynamically query that object.
private Expression<Func<TArg, bool>> CreatePredicate<TArg, TPredicateField>(string fieldName, TPredicateField value)
{
ParameterExpression parameter = Expression.Parameter(typeof(TArg), "o");
MemberExpression memberExpression = Expression.Property(parameter, fieldName);
var condition = Expression.Equal(memberExpression, Expression.Constant(value));
var lambda = Expression.Lambda<Func<TArg, bool>>(condition, parameter);
return lambda;
}
private Expression<Func<TArg, TPredicateField>> CreateSelector<TArg, TPredicateField>(string fieldName)
{
ParameterExpression parameter = Expression.Parameter(typeof(TArg), "o");
Expression propertyExpr = Expression.Property(parameter, fieldName);
var lambda = Expression.Lambda<Func<TArg, TPredicateField>>(propertyExpr, parameter);
return lambda;
}
public TSelectorField GetModStamp<TEntity, TPredicateField, TSelectorField>(TPredicateField id) where TEntity : class
{
using (var ctx = new OnTheFlyEntities("Data Source=(local);Initial Catalog=AscensionBO;Integrated Security=True;MultipleActiveResultSets=True"))
{
var predicate = CreatePredicate<TEntity, TPredicateField>("Id", id);
var selector = CreateSelector<TEntity, TSelectorField>("ModStamp");
TSelectorField item = ctx.Set<TEntity>().Where(predicate).Select(selector).SingleOrDefault();
return item;
}
}
You can then call it like this:
GetModStamp<Entity2, int, string>(1)
If you were willing to just return the found entity, you could eliminate the TSelectorField and then grab the ModStamp from the item after it is retrieved. That will drop one of the expression tree methods and a generic input on the main method.
As someone else suggested, you could go the interface route and use that example, it will be much simpler.

Get collection property of a specific type

I have a class MyDatabaseContext that has a series of DbSet collection properties:
public DbSet<EntityA> EntitiesA { get; set; }
public DbSet<EntityB> EntitiesB { get; set; }
public DbSet<EntityC> EntitiesC { get; set; }
I need to get the name of the collection given the type of the entity.
For example, I have "EntityB" and want to get as a result "EntitiesB".
I really wanted to avoid switch-case statements, since MyDatabaseContext is generated automatically (T4 templates).
if you just want the name of the property here you go. I would just refine the answer given by hunter. You can use the same method with string as return type.
public string GetEntitiName<T>() where T : class
{
PropertyInfo propInfo = typeof(MyDatabaseContext).GetProperties().Where(p => p.PropertyType == typeof(DbSet<T>)).FirstOrDefault();
string propertyName = propInfo.Name; //The string has the property name ..
return propertyName;
}
I tried a sample similar to your situation. Try replacing List with DbSet.
class Program
{
public static void GetEntities<T>() where T : class
{
var info = typeof(TestClass1).GetProperties().Where(p => p.PropertyType == typeof(List<T>));
Console.WriteLine(info.FirstOrDefault().Name);
}
static void Main(string[] args)
{
GetEntities<int>();
Console.ReadLine();
}
}
public class TestClass1
{
public List<int> IntTest { get; set; }
public List<double> DoubleTest { get; set; }
public List<string> IStringTest { get; set; }
}
This sample works.
I know this is old page, But my answer maybe useful for other guys referring here. (like me)
I think you want to accessing EntitiesB to run a query on it, like EntitiesB.Where(a=>a.bla=="blabla"). If I'm right or another visitor of this page needs something like this, just easily use the following code:
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
((IObjectContextAdapter)_dbContext).ObjectContext.CreateObjectSet<EntityB>()
Description:
_dbContext is Context class inherting from DbContext.
EntitiesB is DbSet<EntityB> defined in Context class.
Example:
Ilist result = ((IObjectContextAdapter)_dbContext).ObjectContext.CreateObjectSet<EntityB>().Where(b=>b.bla=="blabla").ToList();
Your generated file is a partial class, you could create a new file and declare a class with same name using the keyword partial, then make a method which will return the desired Collection...
I haven't actually done this myself, but it sounds like what you want to do is to use reflection to locate the property of type "DbSet" that has the appropriate generic type parameter. The following pseudo-C# should get you started:
foreach ( FieldInfo field in this.GetType() )
{
if ( field.FieldType.IsGenericType )
{
foreach ( Type param in field.FieldType.GetGenericArguments() )
{
if ( param.Name == soughtType )
{
return field.Name;
}
}
}
}

Accessing C# property name or attributes

I would like to automatically generate SQL statements from a class instance. The method should look like Update(object[] Properties, object PrimaryKeyProperty). The method is part of an instance (class, base method - generic for any child). Array of properties is an array of class properties, that will be used in update statement. Property names are equal to table field names.
The problem is that I can't get property names.
Is there any option to get a property name inside class instance?
sample:
public class MyClass {
public int iMyProperty { get; set; }
public string cMyProperty2 { get; set; }
{
main() {
MyClass _main = new MyClass();
_main.iMyProperty.*PropertyName* // should return string "iMyProperty"
{
I am aware of PropertyInfo, but I don't know hot to get the ID of a property from GetProperties() array.
Any suggestion?
Just wrote an implementation of this for a presentation on lambdas for our usergroup last Tuesday.
You can do
MembersOf<Animal>.GetName(x => x.Status)
Or
var a = new Animal()
a.MemberName(x => x.Status)
the code:
public static class MembersOf<T> {
public static string GetName<R>(Expression<Func<T,R>> expr) {
var node = expr.Body as MemberExpression;
if (object.ReferenceEquals(null, node))
throw new InvalidOperationException("Expression must be of member access");
return node.Member.Name;
}
}
Link to the presentation and code samples.
Also in SVN (more likely to be updated): http://gim-projects.googlecode.com/svn/presentations/CantDanceTheLambda
I found a perfect solution in This Post
public static string GetPropertyName<T>(Expression<Func<T>> propertyExpression)
{
return (propertyExpression.Body as MemberExpression).Member.Name;
}
And then for the usage :
var propertyName = GetPropertyName(
() => myObject.AProperty); // returns "AProperty"
Works like a charm
You can do something like this:
Type t = someInstance.getType();
foreach (MemberInfo mi in t.GetMembers())
{
if (mi.MemberType == MemberTypes.Property)
{
Console.WriteLine(mi.Name);
}
}
to get all the property names for instance's type.
You can get the name (I assume that's what you meant by ID) of a property using PropertyInfo.Name. Just loop through the PropertyInfo[] returned from typeof(className).GetProperties()
foreach (PropertyInfo info in typeof(MyClass).GetProperties())
{
string name = info.Name;
// use name here
}
Since you already have an explicit handle to the specific property you want, you know the name - can you just type it?
Not 100% sure if this will get you what you're looking for, this will fetch all properties with [Column] attribute inside your class:
In the datacontext I have:
public ReadOnlyCollection<MetaDataMember> ColumnNames<TEntity>( )
{
return this.Mapping.MappingSource.GetModel(typeof(DataContext)).GetMetaType(typeof(TEntity)).DataMembers;
}
Fetching the table column-names that are properties inside the class:
MyDataContext db = GetDataContext();
var allColumnPropertyNames = db.ColumnNames<Animal>().Where(n => n.Member.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false).FirstOrDefault() != null).Select(n => n.Name);
Let's say (from the first sample, method update of a class MyClass):
public class MyClass {
public int iMyStatusProperty { get; set; }
public int iMyKey { get; set; }
public int UpdateStatusProperty(int iValue){
this.iMyStatusProperty = iValue;
return _Update( new[iMyStatusProperty ], iMyKey); // this should generate SQL: "UPDATE MyClass set iMyStatusProperty = {iMyStatusProperty} where iMyKey = {iMyKey}"
}
{iMyStatusProperty} and {iMyKey} are property values of a class instance.
So, the problem is how to get property name (reflection) from a property without using names of properties as strings (to avoid field name typos).

Categories