I am trying to build a dynamic predicate with Entity Framework by mapping an enum to the column field:
In the where clause i have entered ?? as i am not sure what to put there, i want this be dynamic like in this article, although that doesn't work me in EF on;y linq to sql:
How to specify dynamic field names in a Linq where clause?
For example:
I have an enum:
public enum SearchTypes {
FirstName = CustFName,
LastName = CustLName
}
My method is as such:
private static IEnumerable<CustomerSearchInfo> GetCustomers(String customerName, SearchType searchType)
{
using (var context = new NewgenEntities())
{
return context.tblCustomers.Where(??).
Select(p => new CustomerSearchInfo
{
FirstName = p.CustFName,
LastName = p.CustLName,
Id = p.CustID,
EmailAddress = p.CustEmail,
Mobile = p.CustMNumber,
Phone = p.CustPNumber
}).ToList();
}
Has anyone got a way of building an expression based on a enum?
Check out this post for using enums with EF. It's a lot to go through but it works.
Another approach is to create 1 property that is an enum (let's call it SearchType) and then another integer property called SearchTypeId. The enum property encapsulates the Id property like this:
public SearchType SearchType
{
get
{
return (SearchType)this.SearchTypeId;
}
set
{
this.SearchTypeId = (int)value;
}
}
Yes, this is also ugly - but it works.
In the next version of EF, it will support enums but this obviously doesn't do you too much code right now.
Related
I'm trying to do a dynamic query to one of myTables
For that I'm usin the following function:
public async Task<bool> search(DBContext db, M model, string uniqueNonIDField)
{
Type modelType = model.GetType();//get model of generic
object modelInstance = Activator.CreateInstance(modelType);
PropertyInfo field = modelType.GetProperty(uniqueNonIDField); //get property to get existing value
if (field == null)
throw new Exception(string.Format("Campo {0} Não encontrado", uniqueNonIDField));
string value = (string)field.GetValue(model, null); //get value to search in myTable
field.SetValue(model, Regex.Replace(value, #"[\u002D\u2010\u2012\u2013\u2014]", "-"), null); //do some clean up
value = (string)field.GetValue(model, null); //get new value after being cleaned
if (db.Set(modelType).Where(String.Format("#0=#1", uniqueNonIDField, value)).Count() == 0) //Test if there is already any object in myTable with that value.
{...do stuff}
...
}
But there is an error:
System.Linq.Dynamic.ParseException: No property or field '0' exists in type 'myTable'
If I hardcode all expression like:
if (db.myTable.Where("existingField=123").Count() == 0){...}
the error persist with:
System.Linq.Dynamic.ParseException: No property or field '123' exists in type 'myTable'
I'm trying to do as exemplified in many examples and saw many other stackoverflow similar answers, but can't find the reason for the error. Rookie mistake probably.
Can you please help me finding it?
You can pass a string to the where statement. System.Linq.Dynamic allows you to filter by string.
Everything seems to be okay in your code and maybe your issue is related to this one:
https://github.com/zzzprojects/System.Linq.Dynamic/issues/101
Are you using this code under dotnetcore? If yes then please install the related NuGet package and change the using to System.Linq.Dynamic.Core
You cannot pass your where statement as a string like this: Where("existingField=123"), you do it as an expression Expression<Func<TSource,bool>> predicate. In order to pass that expression you will have to know the type for which you want to filter
EDIT: Sample Where
Lets say you have MyClass in your DbContext like this:
public class MyClass
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyContext : DbContext
{
public DbSet<MyClass> MyClass { get; set; }
}
Then to filter on the Name or Id property of MyClass, all you do is:
var filteredEntities = dbContext.MyClass.Where(x => x.Name == "test").ToList();
I have a database table in SQL Server and I need to configure a .NET application to use some of the data in this table, this data is used in a SOAP API call.
For example, the table has these columns
FirstName | LastName | EmailAddress
The values in these columns need to map to certain fields in the API call. This configuration must be flexible, so I cannot just assign FirstName to apiobject.firstname, for example.
Is there a way to derive a string to a property of an object?
For example, for FirstName, would it be possible to resolve it to object.Firstname = value but not doing it like this
if(column == "FirstName")
{
ApiObject.FirstName = columnValue
}
Rather I would want to do something like
ApiObject.[somehow-resolve-which-field] = columnValue
Does anyone know what I am on about?
If you does not have notation of this class and you simply want to create an object with required properties - the right way is to use dynamic, as it noted in other answers.
If you already have a class, for example:
public class MyClass
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
}
You can do what you want with reflection as following:
var myObject = new MyClass();
var prop = myObject.GetType().GetProperty(column);
prop.SetValue(myObject, columnValue);
Where column and columnValue are from your example. Note that column should be of string type and columnValue should match the type of property.
If I correctly understood you need an object which you don't know how many properties have it and what are these properties. For this situations there is dynamic keyword in c#. You can initialize your ApiObject as dynamic
dynamic ApiObject = new ExpandoObject();
then you can set properties of this object as following:
ApiObject.YourProperty=columnValue;
more details: About ExpandoObject
UPDATE
if you want to know which column is this, there is a ColumnName of DataTable of System.Data namespace. There are some issues for getting columns of table in c#. One of them is:
How do I get column names to print in this C# program?
So I am new to C#, LINQ, and MVC. I am trying to get a list of Ages, but it says
The specified type member 'Age' is not supported in LINQ to Entities.
Only initializers, entity members, and entity navigation properties
are supported.
For a previous tutorial, they use this exact same logic, except they check a string, not an int (Age). Why is this giving me a fit, and how can I fix it?
public ActionResult SearchIndex(string ageValue, string searchString)
{
if (!string.IsNullOrEmpty(ageValue))
{
var AgeList = new List<string>();
var AgeListQry = from d in db.Actors orderby d.Age select d.Age.ToString();
AgeList.AddRange(AgeListQry.Distinct());
}
// other stuff
}
I want to learn what is going on, so that I can avoid this in the future!
Entity Model code
public class Actor
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime BirthDate { get; set; }
public int Age
{
get {
return (int)(DateTime.Now - BirthDate).TotalDays / 365;
}
}
public decimal NetValue { get; set; }
}
public class ActorDBContext : DbContext
{
public DbSet<Actor> Actors { get; set; }
}
As mentioned in the comments, you can't call ToString() in a Linq to Entities query. Instead do it like this:
var AgeList = new List<string>();
//retrieve as whatever type Age is, no conversion in SQL Server
var AgeListQry = (from d in db.Actors orderby d.Age select d.Age).ToList();
//convert them after the fact, using Linq to Objects
AgeList.AddRange(AgeListQry.Select(a => a.ToString()).Distinct());
EDIT
I saw your latest update that does show that Age is not a database column. You are then required to do something like this (assuming BirthDate is properly mapped):
var AgeList = new List<string>();
//retrieve BirthDate from SQL Server and use ToList() to get it to run immediately
var AgeListQry = (from d in db.Actors orderby d.BirthDate select d.BirthDate).ToList();
//convert them after the fact, using Linq to Objects
AgeList.AddRange(AgeListQry.Select(bd => ((int)(DateTime.Now - bd).TotalDays / 365).ToString()).Distinct());
Linq to Entities maps your expressions to SQL statements and there is nothing for it to map to when you use your Age property. Instead, you need to get what you can from SQL Server (BirthDate) and then do the translation to Age yourself. You could replace the inline code with a method call like this if you'd rather:
AgeList.AddRange(AgeListQry.Select(bd => CalculateAge(bd)).Distinct());
//...
private string CalculateAge(DateTime birthday)
{
return ((int)(DateTime.Now - bd).TotalDays / 365).ToString();
}
You haven't the Age in you DB scheme and it is impossible to convert LINQ to DB query.
You must order the Age collection in client side or add calculated column to your table.
There is another way. Have a converter file, where you pass the object, works with the birthdate and produces the age, returns the same object. That also means, that you can't search the database for the age column
Not sure of the correct terminology, but is there any way to include a "coded field" that doesn't exist in the table, in an Entity Framework 5 LINQ query?
For example, in SQL you can do this:
SELECT 'Ledger' AS type, (...) FROM table
So that the query result includes a field called 'type' with the value 'Ledger'. Is there a way to do this in a LINQ query?
And before you ask why it has to be in the query, it's because the query is a union of multiple tables and I need a designation of which table the data came from. Without this I will need to query each one separately then merge them.
Yes, you can totally do this.
p.s.w.g has used something that is called an anonymous type. Basically you define the outcome of the query on the fly.
As you can see in his answer, in expression syntax this looks like Select(r => new { type = "Ledger", ... });
In query syntax it looks like this:
from xxx in y
select new { type = "Ledger" };
Behind new, there is no class / type or anything. Neither is type defined. But it will compile as a string natuarlly.
On the other hand you can define a custom ViewModel class for this
public class CustomResultVM
{
//ignoring getter and setter
public int Id;
public string type;
public string name;
}
your select would now be strongly typed and look like this:
Select(r => new CustomResultVM
{
Id = r.xxx,
type = "Ledger",
Name = r.xxxx
});
//query snytax
from xxx in y
select new CustomResultVM
{
Id = r.xxx,
type = "Ledger",
Name = r.xxxx
};
both ways are valid and it depends on what you need at any given point and time.
Sure, you can. It would look a bit like this:
var results = dbContext.Table.Select(r => new { type = "Ledger", ... });
Or if you need a named type, something like this should work:
public class UnionResult
{
string Type { get; set; }
...
}
var results = dbContext.Table.Select(r => new UnionResult { Type = "Ledger", ... });
C# 3.0, Nhibernate 2.1.2 , Castle ActiveRecord 2.1, WinXP 32
I have a problem filtering elements with ActiveRecord and DetachedCriteria.
There are 2 tables one contains the objects to be filtered (PropertyContainer) and the other contains the values of dymamic property set for this object (PropertyValue).
PropertyContainer
Id int
PropertyValue
Id int
ContainerId int
Value real
I need to select PropertyContainer object with the values from PropertyValue table matching some condition (e.g. property with Id = 1 and Value > 2). I would like to do this using DetachedCriteria, I am trying to write something like this:
var detachedCriteria = DetachedCriteria.For(typeof(PropertyContainer));
detachedCriteria.SetProjection(
Projections.SqlProjection(#"select Value from PropertyValue where Id=1"),
new[] { "ExternalProperty" },
new[] { NHibernateUtil.Double }));
detachedCriteria.Add(Expression.Ge("ExternalProperty",2));
var filteredItems = PropertyContainer.SlicedFindAll(0,100,detachedCriteria);
Then this call is executed I get the following error:
"could not resolve property: ExternalProperty of: PropertyContainer"
The question is:
What is wrong with this approach ?
What is the right way to do filtration by dynamic property set using ActiveRecord/NHibernate and DetachedCriteria ?
if PropertyValue looks like:
class PropertyValue
{
public virtual int Id { get; set; }
public virtual double Value { get; set; }
}
you can do:
DetachedCriteria.For<PropertyContainer>()
.CreateAlias("PropertyValues", "prop")
.Add(Restrictions.Ge("prop.Value", 2))
.Add(Restrictions.Eq("prop.Id", 1));