Reusable functions/expressions in LINQ to Entities select clauses - c#

I have a lot of code that casts entities to data transfer objects similar to this:
var result = from t in DatabaseContext.SomeTable
where t.Value1 = someValue
select new SomeTableDTO()
{
Value1 = t.Value1,
Value2 = t.Value2,
SomeParent = new SomeParentDTO()
{
Value3 = t.SomeParent.Value3,
Value4 = t.SomeParent.Value4
}
};
This works, but the problem is I am repeating the same code over and over again because there are many DTOs that have a SomeParent property.
var result = from t in DatabaseContext.SomeOtherTable
where t.Value5 = someValue
select new SomeOtherTableDTO()
{
Value5 = t.Value5,
Value6 = t.Value6,
SomeParent = new SomeParentDTO()
{
Value3 = t.SomeParent.Value3,
Value4 = t.SomeParent.Value4
}
};
I'd like to do something like this so the conversion for the SomeParentDTO can be shared:
var result = from t in DatabaseContext.SomeTable
where t.Value1 = someValue
select new SomeTableDTO()
{
Value1 = t.Value1,
Value2 = t.Value2,
SomeParent = SomeParentConverter(t.SomeParent)
};
.
Func<SomeParent, SomeParentDTO> SomeParentConverter = (parent) =>
{
return new SomeParentDTO()
{
Value3 = parent.Value3,
Value4 = parent.Value4
};
};
But of course this doesn't work because Invoke is not supported in LINQ to Entities. This posting seems to be going in the direction I want, but that uses a single Expression that is passed to .Select(), and that doesn't solve my DRY problem.
Is there any way I can accomplish what I want?

You can use Automapper. It supports mappings in IQueryable extensions.
Part of the sample from the documentation:
public List<OrderLineDTO> GetLinesForOrder(int orderId)
{
Mapper.CreateMap<OrderLine, OrderLineDTO>()
.ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name);
using (var context = new orderEntities())
{
return context.OrderLines.Where(ol => ol.OrderId == orderId)
.Project().To<OrderLineDTO>().ToList();
}
}

Related

How to mock which is returning list of integers on some where condition?

I blocked in below step
var ids = _repository.GetIQueryable<Customers>().Where(lrt => lrt.IsActive == true &&
lrt.NextRoleId == defaultRoleSetting.RoleId &&
lrt.NextUserId == null).Select(x => x.MasterId).Distinct().Take(100).ToHashSet();
I tried this but I don't find the right Returns syntax
_mockRepository.Setup(s => s.GetIQueryable<Customers>()).Returns<List<int>>(ids =>
{
return ????;
});
As you are creating a setup for GetIQueryable<Customers> you'd not return a list of integers, but instead an IQueryable of Customers objects that are filtered afterwards:
IQueryable<Customers> models = new Customers[] {
new Customers() { MasterId = 1, IsActive = true, NextRoleId = nextRoleId, ... },
new Customers() { MasterId = 2, IsActive = false, NextRoleId = nextRoleId, ... },
new Customers() { MasterId = 3, IsActive = true, NextRoleId = nextRoleId, ... },
}).AsQueryable();
_mockRepository
.Setup(s => s.GetIQueryable<Customers>())
.Returns(models);
In this sample, you create an array of Customers objects and set the properties of the customers so that the filter afterwards works on the IQueryable. Which properties to set on the Customers objects depends on the classes and your test case.
by below code my issue solved thanks every one.
var fakeCustomers = FakeCustomers();
_repository.Setup(s => s.GetIQueryable<Customers()).Returns(fakeCustomers.AsQueryable());
private List<Customers> FakeCustomers()
{
string fakeData = #"[{
'Id': '118',
'CreatedBy':'00000000-0000-0000-0000-000000000000',
'SId':'4',
'UserId':'00000000-0000-0000-0000-000000000000',
'NId':'7'
}]";
return JsonConvert.DeserializeObject<List<Customers>>(fakeData);
}

Reuse select new object in linq statement

Below is my linq code and it works. My question is how I can "reuse" those part new ContactResponse, and new AddressResponse in a function to reuse it in another query?
var queryset = (
from a in _repoWrapper.Workshop.FindAll()
where (a.IsActive == true && a.Entity.EntityType.Code == Global.EntityTypeServiceCenterCode)
select new ServiceCenterResponse
{
Id = a.Id,
Name = a.Name,
EntityId = a.EntityId,
Contacts = a.WorkshopContacts.Select(p => new ContactResponse
{
Id = p.Contact.Id,
Type = p.Contact.ContactType.Description,
Code = p.Contact.ContactType.Code,
Value = p.Contact.Value
}).ToList(),
Addresses = a.WorkshopAddresses.Select(p => new AddressResponse
{
Id = p.Address.Id,
AddressType = p.Address.AddressType.Code,
StreetLine1 = p.Address.StreetLine1,
StreetLine2 = p.Address.StreetLine2,
City = p.Address.City,
State = p.Address.State,
PostCode = p.Address.PostCode,
Country = p.Address.Country,
Longitude = p.Address.Longitude,
Latitude = p.Address.Latitude,
Others = p.Address.Others
}).ToList()
}
);
If I correct understand your question, then try this:
Func<WorkshopContact, ContactResponse> contactResponseProjection= p => new ContactResponse
{
Id = p.Contact.Id,
Type = p.Contact.ContactType.Description,
Code = p.Contact.ContactType.Code,
Value = p.Contact.Value
};
And use:
...
Contacts = a.WorkshopContacts.Select(contactResponseProjection).ToList(),
...
Linq has a parameter of type Func in the Select method. This means that you can pass a method to it.
Let me try to do an example.
List<int> list = new List<int> { 1, 2, 3 };
list.Select(AddOne);
where AddOne is a method that you can declare and must have a parameter of type int and a return of whatever you'd like to return. eg.
public int AddOne(int value)
{
return value + 1;
}

LINQ Conditional Where Clauses not working

Using: MVC 5, C#, VS 2013, EF6 with CodeFirst, SQL Server 2012
I have tried the four different ways to get the data without any issues.
IQueryable<vw_Results> qryResults = _db.vw_Results;
The problem I am encountering is that I have 13 filter options and the code for all 13 follow the same logic:
string fmVal = string.Empty;
if (!string.IsNullOrEmpty(form["Locations"]))
{
fmVal = form["Locations"].ToString();
qryResults = qryResults.Where(w => w.LOCATION.CompareTo(fmVal) == 0);
}
if (!string.IsNullOrEmpty(form["ddActionLevels"]))
{
//qryResults = qryResults.Where(w => w.PAL_ID==form["ddActionLevels"].ToString());
vbVal = form["ddActionLevels"].ToString(); ;
//qryResults = qryResults.Where(w => w.AL == vbVal);
qryResults.Where(w => w.AL.CompareTo(vbVal) >= 0);
}
if (!string.IsNullOrEmpty(form["btnGenericRpt"]))
{
qryResults.Where(w => w.LOCATION != "BB1");
}
if (!string.IsNullOrEmpty(form["ddProjects"]))
{
vbVal = form["ddProjects"].ToString();
qryResults.Where(w => w.PROJECT == vbVal);
}
//...
myModel.Results = qryResults.ToList();
return View(myModel);
If I only provide 1 filter, I get the data that I want. As soon as I provide more than 1 filter, I get the "Enumeration yielded no results" yet the data-set from the first filter does contain the data I am filtering on.
The main problem I see with your code is the lines like this:
qryResults.Where(w => w.AL.CompareTo(vbVal) >= 0);
You start with qryResults, compute a .Where(...) but you don't re-assign the query back to qryResults.
Try this:
qryResults = qryResults.Where(w => w.AL.CompareTo(vbVal) >= 0);
Although this doesn't explain why you're getting no results back. That'll be a question you should tackle when you get the code right.
To get filtered data and work from there I needed to add .ToList(), after Where and assign filtered results further:
class Program
{
static void Main(string[] args)
{
var testList = new List<Test>()
{
new Test() { Field4 = false, Field1 = 19845623, Field3 = 1658006 },
new Test() { Field4 = false, Field1 = 19845645, Field3 = 1658056 },
new Test() { Field4 = false, Field1 = 19845665, Field3 = 1658045 },
new Test() { Field4 = false, Field1 = 19845678, Field3 = 1658078 },
new Test() { Field4 = false, Field1 = 19845698, Field3 = 1658098 },
};
var test = testList.Where(x => x.Field4 == false).ToList();
Console.WriteLine();
}
}
internal class Test
{
public int Field1 { get; set; }
public long Field3 { get; set; }
public bool Field4 { get; set; }
}
Hope this is helpful!
Instead of doing multiple where clauses like you code exhibits, you could try using a predicatebuilder ( ref http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx) to build the expression for your where clause based on different if statements and then use it on qryResults at the end.
Here is an example of how you can use predicateBuilder
void Main()
{
var myQuery = new List<Car> {
new Car {IsRed = true, IsConvertible = true },
new Car {IsRed = true, IsConvertible = false },
new Car {IsRed = false, IsConvertible = true },
new Car {IsRed = false, IsConvertible = false }
}.AsQueryable();
Expression<Func<Car, bool>> isRed = c => c.IsRed;
Expression<Func<Car, bool>> isConvertible = c => c.IsConvertible;
var isRedConvertible = isRed.And(isConvertible);
var redConvertible = myQuery.Where(isRedConvertible);
}
public class Car
{
public bool IsRed {get;set;}
public bool IsConvertible {get;set;}
}
Wow, I can't believe what the problem/solution was to my issue. Because I was using the same variable (vbVal) in the .WHERE clause, when it was time to get the data, the query was using the last value of vbVal and thus not returning any data back. I'm guessing that LINQ uses ByRef for variables so my query would end up as:
vbVal = form["filter1"]; {"North America"}
qryResults = qryResults.Where (w=>w.col1 == vbVal);
vbVal = form["filter2"]; {"USA"}
qryResults = qryResults.Where (w=>w.col2 == vbVal);
vbVal = form["filter3"]; {"New York"}
qryResults = qryResults.Where (w=>w.col2 == vbVal);
The query back to SQL would be:
Select col1, col2, col3 From dbTable
Where col1 = "New York" and col2 = "New York" and col3 = "New York"
Assigning each filter option to a unique variable solved my problem.
Thank you all for providing solutions.

Build a dynamic where clause over multiple properties

What I have is a List<string> IndexFields which contains a list of property names.
My issue is that I need to build a where clause based on the elements in the list.
So far I have;
var sitem = List1.Where(p => (p.GetType().GetProperty(IndexFields[0])
.GetValue(p, null) as string) == "red").FirstOrDefault();
But that only allows me to specify a single property. What I need is a builder that can build based on all the names in the List<string> IndexFields list.
The most flexible way to create dynamic queries at runtime is by using the Expression API:
For example:-
var type = typeof(T);
var properties = IndexFields.Select(x => type.GetProperty(x));
// x
var paramter = Expression.Parameter(type);
// x.Foo, x.Bar, ...
var leftHandSides = properties.Select(
x => Expression.Property(parameter, x));
// "Baz"
var rightHandSide = Expression.Constant(...);
// x.Foo == "Baz", x.Bar = "Baz", ...
var equalityExpressions = leftHandSides.Select(
x => Expression.Equal(x, rightHandSide));
// x.Foo == "Baz" && x.Bar == "Baz" && ...
var aggregatedExpressions = equalityExpressions.Aggregate(
(x, y) => Expression.AndAlso(x, y));
// x => x.Foo == "Baz" && x.Bar == "Baz" && ...
var lambda = Expression.Lambda<Func<T,bool>>(
aggregatedExpressions, parameter)
var item = List1.Where(lambda).FirstOrDefault();
A huge advantage of building your queries like this is that the resulting expression can still be e.g. translated into SQL for use with Entity Framework, wheras using reflection inside the body of your lambda is really limiting.
I really do recommend taking some time to really understand the expression framework before using it, though. If you can grok what's going on, it saves you a ton of time in the long run.
You can read more at e.g:-
http://www.digitallycreated.net/Blog/37/dynamic-queries-in-entity-framework-using-expression-trees
https://stackoverflow.com/questions/1217539/net-expression-trees-tutorial
If you're looking for something quicker and dirtier, however, you can just go ahead and chain up those Where clauses inside a foreach:-
IEnumerable<T> query = List1;
foreach (var property in IndexFields)
{
// The variable "property" gets hoisted out of local context
// which messes you up if the query is being evaluated with
// delayed execution.
// If you're working in C# 6 though, you don't need to do this.
var localProperty = property;
query = query.Where(
p => (p.GetType().GetProperty(localProperty)
.GetValue(p, null) as string) == "red");
}
var sitem = query.FirstOrDefault();
You can use a PredicateBuilder for this:
var predicate = PredicateBuilder.New<string>();
if (aCondition)
{
predicate = predicate.And(s => s == "this");
}
if (bCondition)
{
predicate = predicate.And(s => s == "that");
}
you can try something like this, worked for me in linqpad
void Main() {
var listFields = new string[] { "Field1", "Field2" };
var listValues = new string[] { "value1", "value2" };
// prepare & show dummy data
var listItems = Enumerable.Range(1, 100).Select(aaIndex => new MyItem {
Name = string.Format("item{0}", aaIndex),
Field1 = string.Format("value{0}", aaIndex % 3),
Field2 = string.Format("value{0}", aaIndex % 7)
});
listItems.Dump();
// apply filtering
var filtered = listItems.Where(aaItem => Enumerable.Range(0, listFields.Length).All(aaIndex => {
var value1 = aaItem.GetType().GetProperty(listFields[aaIndex]).GetValue(aaItem, null);
var value2 = listValues[aaIndex];
if (value1 is IComparable) {
return ((IComparable)value1).CompareTo(value2) == 0;
}
return Convert.ToString(value1) == Convert.ToString(value2);
}));
filtered.Dump();
}
// Define other methods and classes here
class MyItem {
public string Name { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
}
The dynamic linq library worked well for stuff like this:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
It added overloads to take the different clauses as strings ie:
var query = List1.Where("Color1=""Red"" or Color2=""Red""");
In your case you could build the string from your index fields (probably in a loop but simplified here)
var query = List1.Where(IndexFields[0] + "=""Red"" or " IndexFields[1] + "=Red");
For use, download the sample package, and then grab LinqSamples\DynamicQuery\DynamicQuery\Dynamic.cs and compile with your project.

Using Function in Select Clause of Entity Framework Query

I would like to implement the following logic against Entity Frameworks.
var items = from item in myContext
select new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
};
protected int TweakValue(int value)
{
// Custom processing here
return value;
}
This won't work because of the call to TweakValue() in the select clause. I understand that the query is converted to SQL, and that the problem is that TweakValue() cannot be converted to SQL. My question is what is the most economical way to implement this. Do I need a second loop to convert the values?
I'm still trying to get comfortable with LINQ expressions.
The simplest way is probably to just "move" the execution to the client to perform the transformation. In this case you'd just use:
var items = myContext.Select(item => new { item.Value1, item.Value2 })
.AsEnumerable()
.Select(item => new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
});
Note that you don't have to reuse the names for Value1 and Value2 - it's just easiest to do so.
If you really want to use query expressions:
var query = from item in myContext
select new { item.Value1, item.Value2 };
var items = from item in query.AsEnumerable()
select new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
};
If you want to perform filtering first, you can get that to occur in the database by putting the filtering, ordering etc before the call to AsEnumerable(). For example:
var query = from item in myContext
where item.Foo == bar
orderby item.Something
select new { item.Value1, item.Value2 };
var items = from item in query.AsEnumerable()
select new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
};
You don't need a loop, just another projection:
var items = myContext.Select(i => new {
Value1 = item.Value1,
Value2 = item.Value2
})
.AsEnumerable()
.Select(i => new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
});
Edit: Depending on what TweakValue actually does, you can push the whole thing to the server. Riffing on your current example:
public Expression<Func<Item, ItemProjection>> TweakValue()
{
return item => new ItemProjection
{
Value1 = item.Value1,
Value2 = item.Value2 + 0 // or something else L2E can understand...
};
}
Now use it like:
var exp = TweakValue();
var items = myContext.Select(exp);
Note I'm storing exp in a variable so that L2E doesn't try to directly invoke TweakValue in the query, which would fail.
Naturally, this only works if TweakValue does stuff that L2E can do.

Categories