Projection in Linq to SQL and Linq to Entities - c#

I am trying to get the columns dynamically. In NHibernate i can do this:
var list = _session.CreateCriteria(typeof(Person))
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("FirstName"))
.Add(Projections.Property("LastName"))
.Add(Projections.Property("Jersey"))
.Add(Projections.Property("FortyYard"))
.Add(Projections.Property("BenchReps"))
.Add(Projections.Property("VertJump"))
.Add(Projections.Property("ProShuttle"))
.Add(Projections.Property("LongJump"))
.Add(Projections.Property("PersonSchoolCollection"))
)
.SetResultTransformer(new NHibernate.Transform.AliasToBeanResultTransformer(typeof(Person)))
.List<Person>();
What is the equivalent in Linq?

As you also tag linq-to-sql and linq-to-entities I assume you are looking for an equivalent in Linq-to-Sql or Entity Framework. The two answers (so far) would be such equivalents if _session.Query<Person>() were replaced by context.Persons. (Although Darius's answer would throw an exception saying that you can't create entity instances in an entity query).
But apart from the possibility to use Select to create an ad hoc projection, one of the newer features of AutoMapper makes it even easier. Automapper is a very popular tool to map (say: project) a list of types to another list of types. But until recently its drawback was that it only worked on in-memory lists, i.e. the projection did not propagate into the SQL query. So it could not be used to cut back the number of queried fields (as NHibernate projections does).
This problem was described in Stop using AutoMapper in your Data Access Code (the title says it all). But it also offered a preliminary but excellent fix, which was later adopted by Automapper itself.
This feature makes it possible to write very succinct code like:
var dtoList = context.Persons.Project().To<PersonDto>();
(after the mapping between Person and PersonDto was registered in Automapper).
Now the SQL query only contains the fields that are used for PersonDto.

Isn't that would work:
_session.Query<Person>()
.Select(new {FirstName, LastName, Jersey, FortyYard})
.ToList()
.Select(x => new Person() {
FirstName = x.FirstName,
LastName = x.LastName,
Jersey = x.Jersey,
FortyYard = x.FortyYard
}
);

var list = from person in context.Persons
select new Person()
{
FirstName = person.FirstName,
LastName = person.LastName,
Jersey = person.Jersey,
FortyYard = person.FortyYard,
BenchReps = person.BenchReps,
VertJump = person.VertJump,
ProShuttle = person.ProShuttle,
LongJump = person.LongJump,
PersonSchoolCollection = person.PersonSchoolCollection
};

Related

Linq to Entities: Only primitive types or enumeration types are supported in this context

Using Linq to Entities I need to filter an my entity set based on a list of complex objects that represent criteria. For example:
var criteria = new[]
{
new { FirstName = "Bob", LastName = "Smith" },
new { FirstName = "Jane", LastName = "Doe" }
};
I am try to do the following:
var filtered = PersonEntities.where(person => criteria.Any(c => c.FirstName == person.FirstName && c.LastName == person.LastName)).ToList();
This results in an error due to the query builder not being able to process the complex object. I know that if I had an Id value to filter on, I could simply select it into an in-memory list and replace the complex object with it in the linq query criteria. However I have not been able to find a solution when multiple fields are required in the criteria. Any ideas?
As suggested in the comment, in this specific case we can work around the problem easily without having to build a complex Expression tree manually. If your criteria should be what you declared, you can always re-shape it to a list of strings containing firstname and lastname separated by a underscore (this should not be any of the valid characters for name). Then you have just primitive collection/array and can totally use it in the linq query, something like this:
var first_last = criteria.Select(e=> string.Format("{0}_{1}",e.FirstName,e.LastName));
var filtered = PersonEntities.Where(person => first_last.Any(e=>e == person.FirstName + "_" + person.LastName)).ToList();
If building the Expression tree, I guess you have to even touch the so-called ExpressionVisitor, it's quite a long code (at least 7-8 times of what we've done here).

Linq to SQL Internal .NET Framework Data Provider error 1025

Can someone tell me why I can't select new objects from MS SQL database in Entity Framework in this way:
public static Expression<Func<LeaveDay, bool>> IsInDatesRange(DateTime startDate, DateTime endDate){
return ld => ld.StartDate <= endDate && ld.EndDate >= startDate;
}
this.ObjectContext.People.Select(p => new NewPeopleObject
{
Guid = p.Guid,
FirstName = p.FirstName,
LastName = p.LastName,
LeaveDays = p.CalendarData.LeaveDays.AsQueryable()
.Where(LeaveDayExpressions.IsInDatesRange(startDate, endDate))
.Select(ld => new LeaveDaySummary
{
StartDate = ld.StartDate,
EndDate = ld.EndDate,
})
})
Without AsQueryable() I can't compile application, because LeaveDayExpressions.IsInDatesRange is static Expression. I have tried pass only Func to Where clause but it throws Internal .NET Framework Data Provider error 1025. With Expression and AsQueryable on LeaveDays I get this exception:
Code supposed to be unreachable
People is ObjectSet collection with one CalendarData object on one People and CalendarData has EntityCollection set of LeaveDays.
NewPeopleObject is a class with few properties and IEnumarable LeaveDaySummaries collection.
What can I do to pass Expression to Where clause without parsing linq to sql error?
I can't test the exact case (ObjectContext, ObjectSet etc. indicate some older EF version), but I was able to reproduce both aforementioned runtime exceptions in the latest EF6.1.3 (using DbContext and DbSet) as well.
Let forget about Func approach - EF needs to translate the query to SQL, so Expression is a must. Which in turn requires AsQueryable(). So far so good.
The problem is that EF does not like custom methods inside expression tree - it usually has no issues with top level methods, but definitely has issue with nested calls like in the sample (the problematic expression is part of the outer select expression).
I can't tell why, but in most of the cases (including this and when possible) putting the expression into local variable outside of the query and using it inside resolves the issue:
var leaveDayPredicate = LeaveDayExpressions.IsInDatesRange(startDate, endDate);
var result = this.ObjectContext.People.Select(p => new NewPeopleObject
{
Guid = p.Guid,
FirstName = p.FirstName,
LastName = p.LastName,
LeaveDays = p.CalendarData.LeaveDays.AsQueryable()
.Where(leaveDayPredicate)
.Select(ld => new LeaveDaySummary
{
StartDate = ld.StartDate,
EndDate = ld.EndDate,
})
});
For more advanced scenarios (like expression using something from the outer expression), you might need some expression processing library, for instance LINQKit Invoke / Expand / AsExpandable custom extension methods etc.

Handle basic/full retreival of a database object in entity framework

I have a customer table in the db that stores the name, address etc, along with some photo, logo and other images.
I am using entity framework and I would like to know how to tackle the situation where I only want to bring back basic data about the customer in certain situations vs the complete data including images.
Should I have two entities, CustomerBasic and CustomerComplete
OR
have one Customer entity and fill it with two separate methods, FillBasic, FillComplete.
Any best practices? i'm new to EF.
It really depends on your repository structure if any - with EF itself you can just use a projection with the properties you do want to a business object instance that represents a "basic customer", i.e.
var customerBasicList = context.Customers
.Where(...)
.Select( c => new CustomerBasic()
{
FirstName = c.FirstName,
LastName = c.LastName,
}).ToList();
Performance becomes more important when you have a collection property (i.e. images in your example) in your entity. In your case you can take advantage of lazy loading to only materialize those properties when and if needed.
I think what you want can be accomplished by projecting the query with the select clause. For example (taken from this article)
using(AdventureWorksDB aw = new
AdventureWorksDB(Settings.Default.AdventureWorks)) {
var newSalesPeople = from p in aw.SalesPeople
where p.HireDate > hireDate
orderby p.HireDate, p.FirstName
select new { Name = p.FirstName + " " + p.LastName,
HireDate = p.HireDate };
foreach(SalesPerson p in newSalesPeople) {
Console.WriteLine("{0}\t{1}", p.FirstName, p.LastName);
}
}
You can do a lot with this pattern.

Data projection in Entity Framework and Automapper [duplicate]

This question already has answers here:
Does AutoMapper support Linq?
(2 answers)
Closed 1 year ago.
I want to use AutoMapper to construct a ViewModel (flattening - data projection) for use in an ASP.net MVC app.
var tmp = from x in db.Mailings select Mapper.Map<Mailing, MailingViewModel>(x);
return View(tmp.ToList());
Of course, when I try the sample above, I get the EF error "LINQ to Entities does not recognize the method ... method, and this method cannot be translated into a store expression."
I know it's possible to move the .ToList() before the Automapper does its magic, but then I fetch all the fields from the Db (and I only need 3 of 20 fields)
Is it possible to use that in a clean way. Clean = Not all the fields are fetched from the DB, but only the fields necessary for the ViewModel. Is it possible in Automapper? Or perhaps an other library? (without doing it manually ;) )
Yes this is very possible. See here http://www.devtrends.co.uk/blog/stop-using-automapper-in-your-data-access-code
Edit: I've recently found that the basis to this already exists in AutoMapper. add a using statement for AutoMapper.QueryableExtensions and you are provided with an IQueryable extension called Project<>()
You can simply call:
var tmp = from x in db.Mailings
select new MailingViewModel
{
FirstName = x.FirstName,
LastName = x.LastName,
Address = x.Address
};
You don't need AutoMapper for simple projection if you access EF directly in controller.
You can't involve AutoMapper in linq-to-entities query - no way. You must either return entity (or another projected object) and map it by AutoMapper or use plain projection without AutoMapper.
You should be able to do this using AutoMapper's DynamicMap. I believe something like the following sill solve your issue if you really want to use AutoMapper, although in this particular case I agree with Ladislav Mrnka.
var tmp = from x in db.Mailings
select new
{
FirstName = x.FirstName,
LastName = x.LastName,
Address = x.Address
};
return View(tmp.ToList().Select(item => Mapper.Map<MailingViewModel>(item)));
Unfortunately, if you want to limit the columns you're returning from the database you need to specify which ones you want, which does defeat the purpose of AutoMapper in this scenario. This would be a really neat AutoMapper extension though, to take the destination type and dynamically create a select expression based on the type's properties.
This is caused by the way linq interacts with IQueryableProviders ( I think this is the interface ).
So what is happening is that Linq is getting compiled to an expression tree which the underlying linq provider reads and tries to convert to sql. The linq provider has no idea how to translate Mapper.Map<> into SQL hence the error.
For a good video on how linq providers work check out: http://channel9.msdn.com/Shows/Going+Deep/Erik-Meijer-and-Bart-De-Smet-LINQ-to-Anything
This can be done using LINQ Projector library. It is based on http://www.devtrends.co.uk/blog/stop-using-automapper-in-your-data-access-code and adds some mapping conventions.
since Automapper doesn't work directly to database (need to transform to in-memory object before addressing it), I've written own simple class to copy identical properties:
Initial code:
model.Sales = _dbcontext.Sales.Where(o => o.PartnerId == PartnerId && (o.SaleDate > model.BeginDate || model.BeginDate == null) && (o.SaleDate <= model.EndDate || model.EndDate == null)).Select(o => new SaleViewModel
{
NumberIn1S = o.NumberIn1S,
Total = o.Total,
SaleDate = o.SaleDate,
Comments = o.Comments,
Driver = o.Driver,
GuidIn1S = o.GuidIn1S
}).OrderByDescending(o => o.SaleDate).ToList(); </p>
MyMapper:
model.Sales = _dbcontext.Sales.Where(o => o.PartnerId == PartnerId && (o.SaleDate > model.BeginDate || model.BeginDate == null) && (o.SaleDate <= model.EndDate || model.EndDate == null)).OrderByDescending(o => o.SaleDate).ToArray().Select(p => <b>MyMapper<SaleViewModel>.CopyObjProperties(p, "NumberIn1S,Total,SaleDate,Comments,Driver,GuidIn1S")</b>).ToList(); </p>
I think automapper still can work directly with DB
query.ProjectTo<YourObjectDto>(_mapper.ConfigurationProvider).ToList
In my MVC 5 app I inject IMapper and get _mapper.ConfigurationProvider

Can I refactor a range variable in a linq statement?

I have an entity framework 3.5 project where I'm using TPH inheritence. Two of my concrete types are in their own DAO class and contain a selector that projects the Entity Class into a DTO. However, both of these concrete classes have similar relations with another table which I use a let statement to identity clearly. My question is, can I somehow refactor this let statement because as I create more inherited concrete classes I feel I'm violating DRY.
StudentDAO.cs:
var myDTO = from x in context.Students
let address = (from a in x.addresses where a.active == true select a).FirstOrDefault()
select new StudentDTO { id = x.id, studentname = x.studentname, address = a.address};
ProfessorDAO.cs:
var myDTO = from x in context.Professors
let address = (from a in x.addresses where a.active == true select a).FirstOrDefault()
select new ProfessorDTO { id = x.id, professorname = x.studentname, address = a.address};
so is there a way I can refactor the address out of both of these queries?
Short answer: I'm afraid there isn't much you could easily do. The problem is that you need to parameterize the query. The query however needs to be represented as an expression tree (so that it can be translated to SQL or whatever EF uses). Unfortunately, C# don't provide any elegant ways for composing expression trees.
Long answer: Some time ago, I wrote a C# article that explains how to compose queries (using some tricks) - you can take expression tree and splice it into a parameterized query. So, in principle you could use the techniques from the article to create a method that takes the changing parts of the expression tree and then compose the query.
I'll use the types used in LINQ to SQL, because I'm more familiar with it, but I believe the principle should be the same:
IQueryable<R> GetInfo<T>(IQueryable<T> source, // e.g. context.Students
Expression<Func<T, IQueryable<Address>>> getAddr, // x => x.adresses
Expression<Func<T, Address, R>> getRes // (x, a) => new StudentDTO { ... }
return
from x in source.AsExpandable()
let address = (from a in getAddr(x).Expand() where a.active == true
select a).FirstOrDefault()
select getRes(x, a).Expand();
}
// The use would look like this
var res = GetInfo(context.Professors, x => x.addresses, (x, a) => new
ProfessorDTO { id = x.id, professorname = x.studentname, address = a.address });
So a summary is that C# and LINQ do not provide any built-in support that would make it easier. However, with some effort you can write query that is parameterized by some parts of the expression tree. In your case, it makes the code a little shorter and less repetitive, but it makes it a lot more complex (and you also need to use library like the one I referenced that provides AsExpandable and Expand).
In this case I'm afraid the it's not worth the effort. But it would be nice if C# v(Next)^x provided some more elegant support for things like this :-)

Categories