I have a POCO class describing my model:
public class Example
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
}
What I'm trying to do is an extension method to project my class this way using Entity Framework DbSets:
var qry = db.Examples.Select(x => new {
Prop1 = x.Prop1,
Prop2 = x.Prop2,
Prop3 = x.Prop3,
Description = XXXXX
}).ToList();
Where XXXXX is the value of the Prop1, Prop2 or Prop3 property, wich name I now as an string only at runtime.
I cannot use Dynamic Linq, because I'm targeting Entity Framework Core, and I'm getting crazy with LINQ expressions and I think I'm still far from the solution...
Could you provide some guidance, please?
As you fetch all needed properties for Description explicitly, you can fetch the query without Description and then generate the desired query from the loaded data.
Assuming the name of the property for setting Description is stored in name variable:
var qry1 = db.Examples.Select(x => new {
Prop1 = x.Prop1,
Prop2 = x.Prop2,
Prop3 = x.Prop3,
}).ToList();
var qry = qry1.Select(x => new {
Prop1 = x.Prop1,
Prop2 = x.Prop2,
Prop3 = x.Prop3,
Description = name == "Prop1"? x.Prop1 : name == "Prop2"? x.Prop2: x.Prop3
}).ToList();
If you don't like hard coding the names, you can use reflection to get the value:
Description = GetProp(x, name)
where GetProp is:
private string GetProp(object y, string name)
{
return y.GetType().GetProperty(name).GetValue(y).ToString();
}
Related
The code is in Alpha version, don't have validations or error management ... It would be included later.
I have a very simple model with two related entities: Location and Country:
public class UNCountry
{
public int UNCountryId { get; set; }
[Required]
[MaxLength(2, ErrorMessage = "The field {0} only can contains a maximum of {1} characters lenght.")]
[RegularExpression("[A-Z][A-Z]", ErrorMessage = "The field {0}, This code is formed by Two(2) capital letters")]
public string Code { get; set; }
[Required]
[MaxLength(255, ErrorMessage = "The field {0} only can contains a maximum of {1} characters lenght.")]
public string Name { get; set; }
}
public class UNLocation
{
public int UNLocationId { get; set; }
[MaxLength(1, ErrorMessage = "The field {0} only can contains a maximum of {1} characters lenght.")]
public string UNChangeType { get; set; }
[Required]
[MaxLength(5, ErrorMessage = "The field {0} only can contains a maximum of {1} characters lenght.")]
[RegularExpression("[A-Z]{5}", ErrorMessage = "The field {0}, This code is formed by 5 capital letters")]
public string Code { get; set; }
[Required]
[MaxLength(255, ErrorMessage = "The field {0} only can contains a maximum of {1} characters lenght.")]
public string Name { get; set; }
public int UNCountryId { get; set; }
public UNCountry UNCountry { get; set; }
}
I have an app that list all the locations and the user can filter by any column, this is a generic Component View in Angular and I want to have all the Column Filters be Dynamic. The filters are passed in JsonFilters Parameter as an array
[{name:"nameOfField1", value:"valueOfField1"},...],
then I construct a predicate with LinqKit and PredicateBuilder, and use the predicate in the query .Where(predicate). It works perfectly.
Here is the initial code in the controller
var validFilter = new PaginationFilter(
pF.PageNumber, pF.PageSize, pF.JsonFilters
);
var p = validFilter.CreateDynamicSearch<UNLocation>();
//var b = PredicateBuilder.New<UNLocation>(true);
//b = b.And(c => c.UNCountry.Name.Contains("Col"));
var pagedData = await _context.UNLocations
.Include(c => c.UNCountry)
.Where(p)
//.Where(d => d.UNCountry.Name.Contains("Col"))
.OrderBy(x => x.UNLocationId)
.Skip((validFilter.PageNumber - 1) * validFilter.PageSize)
.Take(validFilter.PageSize)
.ToListAsync();
var totalRecords = await _context.UNLocations.CountAsync();
return Ok(new PagedResponse<List<UNLocation>>(pagedData, validFilter.PageNumber, validFilter.PageSize,totalRecords));
I pass the fieldname and the value in the filters parameter, and all works well, but when I have a filter over the name of the country (the field name is UNCountry.Name) the predicate fails using this name of field over UNLocations, it fails in the function that constructs the predicate, but if I use in the Where directly (The commented line //.Where(d => d.UNCountry.Name.Contains("Col")) in the previous code) It Works, I could be solve the problem like this, but it would not be dynamic for other Views with more related data fields in query.
Here is the predicate generation function:
public Expression<Func<T, bool>> CreateDynamicSearch<T>()
{
var predicate = PredicateBuilder.New<T>(true);
foreach (ReqFilter filter in this.Filters)
{
var columnFilter = PredicateBuilder.New<T>(false);
var param = Expression.Parameter(typeof(T), "a");
string[] filterParts = filter.Name.Split(".");
string filterName = filterParts[0];
if (filterParts.Length == 1)
{
var prop = Expression.Property(param, filterName);
var call = Expression.Call(prop, "Contains", new Type[0], Expression.Constant(filter.Value));
columnFilter = columnFilter.Or(Expression.Lambda<Func<T, bool>>(call, param));
predicate = predicate.And(columnFilter);
}
else
{
var prop = Expression.Property(param, filter.Name);
var call = Expression.Call(prop, "Contains", new Type[0], Expression.Constant(filter.Value));
columnFilter = columnFilter.Or(Expression.Lambda<Func<T, bool>>(call, param));
predicate = predicate.And(columnFilter);
}
}
return predicate;
}
This code works well if i pass the filters with the normal fields of the UNLocation, like code or Name byExample [{code:"MIA"},name:"MI"], but the predicate constructor fails if i send the column name of the related UNCountry Name byExample [{UNCountry.Name:"United States"}], I repeat It works if I write the Where in Design Time code, but i need to have mor flexybility, and generate in predicate. It is possible or how can achieve this ? Thanks in advance, excuse my English.
I need some help here. I am trying to make a portion of an EF Query reusable.
var query = from sr in SomeEFRepository.SelectAll()
select new {
KeyValuePivotField1 = (from kvd in sr.KeyValueData
where kvd.KeyName == "FieldName1"
select kvd.Value).FirstOrDefault(),
KeyValuePivotField2 = (from kvd in sr.KeyValueData
where kvd.KeyName == "FieldName2"
select kvd.Value).FirstOrDefault(),
KeyValuePivotField3 = (from kvd in sr.KeyValueData
where kvd.KeyName == "FieldName3"
select kvd.Value).FirstOrDefault()
}
If you look you can see that I am constantly repeating myself with the following code.
(from kvd in sr.KeyValueData
where kvd.KeyName == "SomeFieldName"
select kvd.Value).FirstOrDefault(),
How can I make a method that EF will recognize in order to do something like this?
var query = from sr in SomeEFRepository.SelectAll()
select new {
KeyValuePivotField1 = GetFieldFromKeyValue("FieldName1"),
KeyValuePivotField2 = GetFieldFromKeyValue("FieldName2"),
KeyValuePivotField3 = GetFieldFromKeyValue("FieldName3"),
}
Any suggestions will help. I have been reading about Expression Trees but not sure if
that would be a good approach or even possible.
Note: I am using EF 5.0 with DBContext.
Extension methods seem like a good fit for this. I don't know exactly what you are doing so this is a example
Customer class
public class Customer{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public override string ToString()
{
return string.Format("Customer is {0} {1}", this.FirstName, this.LastName);
}
}
CustomerExtensionMethod
//Class MUST be static
public static class CustomerExtensionMethods
{
//Method MUST be static and use this keyword to be an extension method
public static Customer GetByFirstName(this IEnumerable<Customer> source, string value)
{
return source.Where(c => c.FirstName == value).FirstOrDefault();
}
}
Main program to show how it works
var customers = new List<Customer>
{
new Customer { Id = 1, FirstName = "Foo", LastName = "Bar" },
new Customer { Id = 2, FirstName = "Mark", LastName = "Whoknows" },
new Customer { Id = 3, FirstName = "Ronald", LastName = "McDonald" },
};
var userWithFirstNameOfRonald = customers.GetByFirstName("Ronald");
Console.WriteLine(userWithFirstNameOfRonald.ToString());
This should print "Customer Is Ronald McDonald". The important parts are commented. The Class that contains the method should be static and the method needs to be static. Along with that the first parameter of the method needs to be this to signal it is an extension method. This will allow you to call it on the specific type you put for the first parameter.
Found a great answer and article to my troubles.
http://www.codeproject.com/Articles/402594/Black-Art-LINQ-expressions-reuse
A nice NuGet Package LinqExpressionProjection 1.0.0 that is perfect for my problem.
http://nuget.org/packages/LinqExpressionProjection
I am using EF + RIA and unfortunately meet some problems with sorting by related entities.
For such purpose there is ESQL query that I implemented (found only this solution):
var queryESQL = string.Format(
#" select VALUE ent from SomeEntities as ent
join Attributes as ea ON ea.EntityId = ent.Id
where ea.AttributeTypeId = #typeId
order by ea.{0} {1}", columnName, descending ? "desc" : "asc");
var query = ObjectContext.CreateQuery<SomeEntity>(queryESQL, new ObjectParameter("typeId", attributeTypeId));
Tables have following structure:
<Attribute>:
int Id;
decimal DecimalColumn;
string StringColumn;
int EntityId;
int AttributeTypeId;
<SomeEntity>:
int Id;
string Name;
Is there any way to rewrite this stuff(sorting), using LINQ to Entities approach?
Here's my attempt, I can't guarantee it will work. I need to think more on how to get a dynamic column name, I'm not sure on that one. EDIT: you can use a string for the order column.
int typeId = 1115;
bool orderAscending = false;
string columnName = "StringColumn";
var query = from ent in SomeEntities
join ea in Attributes on ea.EntityId = ent.Id
where ea.AttributeTypeId = typeId;
if(orderAscending)
{
query = query.OrderBy(ea => columnName).Select(ea => ea.Value);
}
else
{
query = query.OrderByDescending(ea => columnName).Select(ea => ea.Value);
}
var results = query.ToList(); // call toList or enumerate to execute the query, since LINQ has deferred execution.
EDIT: I think that ordering after the select stops is from ordering by. I moved the select statement to after the order by. I also added the "query =", but I'm not sure if that is needed. I don't have a way to test this at the moment.
EDIT 3: I fired up LINQPad today and made a few tweaks to what I had before. I modeled your data in a Code-first approach to using EF and it should be close to what you have.
This approach works better if you're just trying to get a list of Attributes (which you aren't). To get around that I added an Entity property to the MyAttribute class.
This code works in LINQPAD.
void Main()
{
// add test entities as needed. I'm assuming you have an Attibutes collection on your Entity based on your tables.
List<MyEntity> SomeEntities = new List<MyEntity>();
MyEntity e1 = new MyEntity();
MyAttribute a1 = new MyAttribute(){ StringColumn="One", DecimalColumn=25.6M, Id=1, EntityId=1, AttributeTypeId = 1, Entity=e1 };
e1.Attributes.Add(a1);
e1.Id = 1;
e1.Name= "E1";
SomeEntities.Add(e1);
MyEntity e2 = new MyEntity();
MyAttribute a2 = new MyAttribute(){ StringColumn="Two", DecimalColumn=198.7M, Id=2, EntityId=2, AttributeTypeId = 1, Entity=e2 };
e2.Attributes.Add(a2);
e2.Id = 2;
e2.Name = "E2";
SomeEntities.Add(e2);
MyEntity e3 = new MyEntity();
MyAttribute a3 = new MyAttribute(){ StringColumn="Three", DecimalColumn=65.9M, Id=3, EntityId=3, AttributeTypeId = 1, Entity=e3 };
e3.Attributes.Add(a3);
e3.Id = 3;
e3.Name = "E3";
SomeEntities.Add(e3);
List<MyAttribute> attributes = new List<MyAttribute>();
attributes.Add(a1);
attributes.Add(a2);
attributes.Add(a3);
int typeId = 1;
bool orderAscending = true;
string columnName = "StringColumn";
var query = (from ent in SomeEntities
where ent.Attributes.Any(a => a.AttributeTypeId == typeId)
select ent.Attributes).SelectMany(a => a).AsQueryable();
query.Dump("Pre Ordering");
if(orderAscending)
{
// query = is needed
query = query.OrderBy(att => MyEntity.GetPropertyValue(att, columnName));
}
else
{
query = query.OrderByDescending(att => MyEntity.GetPropertyValue(att, columnName));
}
// returns a list of MyAttributes. If you need to get a list of attributes, add a MyEntity property to the MyAttribute class and populate it
var results = query.Select(att => att.Entity).ToList().Dump();
}
// Define other methods and classes here
}
class MyAttribute
{
public int Id { get; set; }
public decimal DecimalColumn { get; set; }
public string StringColumn { get; set; }
public int EntityId { get; set; }
public int AttributeTypeId { get; set; }
// having this property will require an Include in EF to return it then query, which is less effecient than the original ObjectQuery< for the question
public MyEntity Entity { get; set; }
}
class MyEntity
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<MyAttribute> Attributes { get; set; }
public MyEntity()
{
this.Attributes = new List<MyAttribute>();
}
// this could have been on any class, I stuck it here for ease of use in LINQPad
// caution reflection may be slow
public static object GetPropertyValue(object obj, string property)
{
// from Kjetil Watnedal on http://stackoverflow.com/questions/41244/dynamic-linq-orderby
System.Reflection.PropertyInfo propertyInfo=obj.GetType().GetProperty(property);
return propertyInfo.GetValue(obj, null);
}
I would like to ask what is "var" in this statement.
var context = new MHC_CoopEntities();
var lists = (from c in context.InventLists
select new
{
c.InventID,
c.ItemName,
c.InventCategory.CategoryID,
c.UnitQty,
c.UnitPrice
}).ToList();
ListGridView.DataSource = lists;
ListGridView.DataBind();
I know that "var" can be any value. I am trying to create a helper class for this.
var has nothing to do with Entity Framework. It's a pure C# construct allowing you to define an implicitly typed object. It's explained in the documentation. Basically it allows the compiler to infer the actual type of the variable from the right handside of the assignment. This avoids you repeating the same type declaration twice. It is also necessary for anonymous types which do not have a name. For example:
var foo = new { Id = 123, Name = "anon" };
And this is exactly what happens in your example. In the select clause you are returning an anonymous type. So the only way is to use var.
In the first example:
var context = new MHC_CoopEntities();
it is equivalent to:
MHC_CoopEntities context = new MHC_CoopEntities();
because we know the type.
It will be a new anonymous type. If you need to pass it between functions, you should declare the custom type first (I've called it InventType):
public class InventType {
int InventId { set; get; }
string ItemName { set; get; }
int CategoryId { set; get; }
int UnitQty { set; get; }
decimal UnitPrice { set; get; }
}
var lists = (from c in context.InventLists
select new InventType
{
InventId = c.InventID,
ItemName = c.ItemName,
CategoryId = c.InventCategory.CategoryID,
UnitQty = c.UnitQty,
UnitPrice = c.UnitPrice
}).ToList();
Now var represents List<InventType>.
I have the following enum and POCO class
public enum Gender
{
Male,
Female,
Unknown
}
public class Person
{
public int PersonId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public Gender? Gender { get; set; }
}
I would like to perform a "get all people" query in my repository such that it would look something like this:
return from p in _db.People
select new Model.Person
{
PersonId = p.PersonId,
LastName = p.LastName,
FirstName = p.FirstName,
Gender = p.Gender,
};
Unfortunately I get an error "Cannot implicitly convert type 'string' to 'Model.Gender'"
I would like to convert the string which is being queried from the entity framework to my Gender enum and assign it to my POCO class.
Enums are not supported in Entity Framework. There is a workaround by Alex James, but it's quite involved.
Instead, i prefer to do this:
public enum Gender : byte
{
Male = 1,
Female,
Unknown
}
public class Person
{
public int PersonId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public byte Gender { get; set; } // this is the EF model property
public Gender GenderType // this is an additional custom property
{
get { return (Gender) Gender; }
set { Gender = (byte)value; }
}
}
It's basically a hook/wrapper for the actual value. In your database, store Gender as a tinyint (which maps to byte on the conceptual side).
Then you can use a byte enum to map to and from the model property:
return from p in _db.People
select new Model.Person
{
PersonId = p.PersonId,
LastName = p.LastName,
FirstName = p.FirstName,
Gender = p.Gender, // sets byte
};
But then if you access that ViewModel, because your setting the byte field for Gender, you will also have access to the enum property GenderType.
Does that solve your problem?
The Entity Framework that I am familiar with does not provide support for enums. EF uses your query expression to create an SQL statement that it then sends to the server, if it cannot create the SQL equivalent of some operation it will throw a NotSupportedException for that operation. If you are expecting to return a small set of data you can separate from the Entity Framework by creating an object in memory using the ToArray method.
var myEntities = (from entity in _db.Entities
where /* condition */
select entity)
.ToArray();
This will create a sequence of entities in memory. Any further query statements will then be in the realm of LINQ to Objects which allows parsing of strings into enums:
return from myEntity in myEntities
select new MyDataContract
{
ID = myEntity.ID,
Gender g = (Gender)Enum.Parse(typeof(Gender), myEntity.Gender, true)
};
Or you could even break it out into a foreach loop:
List<MyDataContract> myDataContracts = new List<MyDataContract>();
foreach (var myEntity in myEntities)
{
var dataContract = new MyDataContract { ID = myEntity.ID };
if (Enum.IsDefined(typeof(Gender), myEntity.Gender))
dataContract.Gender = (Gender)Enum.Parse(typeof(Gender), myEntity.Gender, true);
myDataContracts.Add(dataContract);
}
return myDataContracts.AsEnumerable();
if (Enum.IsDefined(typeof(Gender), genderstring))
Gender g = (Gender) Enum.Parse(typeof(Gender), genderstring, true);
else
//Deal with invalid string.
try
Gender = p.Gender != null ? (Gender)Enum.Parse(typeof(Gender), p.Gender) : (Gender?)null;
To parse the string as one of the enums
here's a workaround but it means changing your nice and clean POCO
http://blogs.msdn.com/b/alexj/archive/2009/06/05/tip-23-how-to-fake-enums-in-ef-4.aspx