Is it possible to map IQueryable<CatDTO> to IQueryable<CatEf>? - c#

This is a question for Automapper professionals. I have tried to do query mapping for several days already - and no luck. Seems like Automapper is not intended to be used the way I want to use it. But maybe I am wrong. So here is the question...
I have such classes:
CatDto (Name, Age, Toys (collection of ToyDto objects))
ToyDto (CatName, ToyName, Cat (parent CatDto object))
Cat (comes from Entity Framework, has properties similar to those in CatDto)
Toy (comes from Entity Framework, has properties similar to those in ToyDto)
I want to write a generic read function in my data access layer, something like this:
IEnumerable<CatDto> Read(IQueryable<CatDto> query) {
// here "query" is converted
// to Entity Framework query by means of AutoMapper,
// EF query gets executed,
// I convert EF entities (Cat) back to CatDto - this is not essential
// result is returned
}
I will call this function in different manners. Example:
var q = new ObjectModel.Collection(Of CatDto)).AsQueryable();
q = q.Where(c => c.Toys.Count() > 1);
var someResultVar = Read(q);
So far any attempts to implement such behavior have failed. I wonder if Automapper is a helper here or am I going completely wrong way?

I believe the functionality you want is in UseAsDataSource
You can't map IQueryable, but you shouldn't need to with UseAsDataSource
Example
IQueryable<CatDto> someResultVar = new ObjectModel.Collection(Of CatDto)).AsQueryable().UseAsDataSource().For(Of OrderLineDTO).Where(c => c.Toys.Count() > 1);
When you enumerate it will convert Lambda from CatDto to CatEf and call ProjectTo<CatDto> and return CatDto objects

Related

Create EF Core Include().ThenInclude() like function?

I'm writing a few simple extension methods to create DTOs out of entities defined in the domain, however, those entities have properties that are also entities, and I'd like to be able to write something (that I personally find elegant) like they do in EF Core with Include().ThenInclude().
Ideally I'd like to be able to write something like
return myEntity.ToDto().Include(entity => entity.SubEntity).ThenInclude(subEntity => subEntity.AnotherSubEntity);
Is it possible?
The idea is that if I just call ToDto() I would simply receive a basic DTO object where all simple type properties are set but all complex type properties are null, unless I specify that I want to inclue one (or more) of the properties too.
AutoMapper Queryable Extensions provides a convenient way to query entities with relationships and map them to DTOs in one step.
It produces an optimized SQL query needed to copy that data.
Example:
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<BlogDTO, Blog>().ReverseMap();
cfg.CreateMap<PostDTO, Post>().ReverseMap();
});
return _context.Blogs.Where(b => b.Id == Id)
.Include(x => x.Posts)
.ProjectTo<BlogDTO>(config)
.ToList();
I've actually managed to do it by sort of copying how EF Core does it with a lot of reflection and expression trees, and by creating my own visitor to then process the whole expression tree.
I am now able to write code like: myEntity.AsDtoable().Include( x => x.ComplexTypeProp ).ThenInclude( x => x.AnotherComplexTypeProp ).ToDto< EntityDto >();

GetValue LINQ Error

I'm trying to retrieve the value for a particular property of an entity into a variable using the following code.
var item = db.Notices
.Where(a => a.ID == 0)
.Select(x => x
.GetType()
.GetProperty("Spell_ID")
.GetValue(x));
I'm just playing around with this at the moment, but at some point I'd like to be able to replace the 'Spell_ID' text with any column name and get the value dynamically. Not sure if I'm going the right way around this, but I'm getting the following error:-
LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object)' method, and this method cannot be translated into a store expression.
I know I'm not doing this the right way (and I'm relatively new to C# MVC / LINQ), but I've spent so much time tinkering with the code I've lost my way...can somebody point me in the right direction please?
Your current code uses reflection to get the value of a property, but, from what I can infer from your exception message, db is an Entity Framework DbContext.
Entity framework does not support reflection at all, because your LINQ query is then converted into a SQL query by the framework itself. For this reason you have to change your approach if you really need to get a single property:
var items = db.Notices.Where(a => a.ID == 0).ToList();
var itemsProperty = items.Select(x => x.GetType().GetProperty("Spell_ID"));
This will fetch all the resources from the database and then execute the Select part in memory.
If you expect only a single entity from your database than this is a better approach:
var entity = db.Notices.SingleOrDefault(a => a.ID == 0);
var property = entity.GetType().GetProperty("Spell_ID");
Not to be tounge-in-cheek, but the error
LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object)' method, and this method cannot be translated into a store expression.
is exactly what it sounds like. LINQ is unable to translate the GetValue() method to whatever it is that Entity Framework does exactly.
While there are ways to get EF and LINQ to recognize methods, its kinda a pain. The quickest solution would be to just use a loop.

What does entity framework .load(MergeOption) do?

I'm trying to upgrade a large application which uses Entity Framework 4 to use Entity Framework 5.
I've discovered a function like this:
public FooModel(FooEntity foo)
{
_foo = foo;
_foo.bars.Load(System.Data.Objects.MergeOption.OverwriteChanges);
}
Where foo and bar are generated entities, with bar having a foreign key to foo.
It seems EF5 no longer has the .Load(MergeOption) function, and I've never seen it before.
Does anyone know what it does, and what its equivalent is?
https://stackoverflow.com/a/13178313/784908 suggests that Load is part of DbContext - but my entity container inherits from DbContext, and still isn't available
My best guess is that it is used for Eager loading of the foreign keys (which I need to do, the context is created and disposed of many times in a request, and there is no guarentee it will exist/attached when FooModel is used)
Entity Framework - eager loading of related entities shows I should be using .Include(), but that function doesn't seem to be available on an actual entity (I think the term is 'materialized query'?)
Thanks for reading
.Load() loads an IQueryable query from database into the memory - in fact, Local property of the relevant entity of your DbContext.
You can use this method with any IQueryable collection, not just the DbContext. Examples are as the following:
var q = db.Products.Include("Category").ToList();
q.Load(); // -> you can't!
// ------------
db.Products.Include("Category").Load(); // It's OK!
// This will NOT query the database, just looks in-memory data.
var p = db.Products.Local.Single(id);
// ------------
var q = db.Products.Include("Category").ToList();
q.AsQueryable().Load(); // -> It's OK!
// This also will NOT query the database, just looks in-memory data.
var p = db.Products.Local.Single(id);
The load function is mainly used for 2 reasons:
1) Retrieving some parts of data from Db to memory and work with them:
db.Products.Include("Category").Where(p => p.CatId == 10).Load();
2) To be able to use Linq-to-Objects methods which L2E doesn't supports(like .ToString(), etc.) - as the Local property of DbContext entities are ObservableCollection<T> which implements IEnumerable, just as L2O objects:
db.Products.Include("Category").Where(p => p.CatId == 10).Load();
string subName = db.Products.Local.Find(id).SubString(0, 4);

EF - Error in converting type Dataaccess.Employees to ViewModels.Employee

I have a view model class called Employee and a EF entity called Employees. If I write the below query I get an error stating that "cannot convert types, an explicit conversion already exists) I know how to solve this using select new ViewModel.Employee clause, but was wondering is there any concise way to resolve this ? Any help would be greatly appreciated -thanks
var selectedEmployee = (from q in emsCtx.Employees
where q.Id == employee.Id
select q).ToList().FirstOrDefault();
employeeData.Employees = selectedEmployee;
If you have two different types, even if they have the same properties, you need to do the mapping between them. You can do this in many different ways, however a tool like AutoMapper can help. AutoMapper is very good if you have types that are largely (or completely) similar in what public properties they have.
AutoMapper will allow you to make a mapping configuration between the two types and then map them.
For example, something like this:
Mapper.CreateMap<DataAccess.Employee, ViewModels.Employee>();
var dataEmployee = // data access
var viewModelEmployee = Mapper.Map(dataEmployee);

EF Table Per Type with Navigation property on Sub Type Table

I'm hoping someone out in the SO community will be able to help me out here.
Simplified Background:
I'm using Entity Framework V1 to build my class structure that is outlined below, I'm using Table Per Type to persist my inherited objects:
Employee
CaseA : Case
CaseB : Case
CaseC : Case
CaseB has a Navigational Property to Employee
I have a Repository that returns an ObjectQuery. If the type of Case is actually CaseB, I need to include the Employee object within the graph. I can't .Include("Employee") because it's not a navigational property of Case, and Employee doesn't have a .Load() method on it.
Ideally I want to be able to do this in one query, however as a fall back I'm happy that I make a call, check the Object and perform another call, something like this: (although as I stated earlier, load doesn't exist on the employee navigational property)
//Get the case from the
Case myCase = new Repo<Case, Entities>.FirstOrDefault();
if(myCase is CaseB)
((CaseB)myCase).Employees.load();
Am I missing something really simple here?
Try this:
var employee = ctx.Cases
.OfType<CaseB>()
.Include("Employees")
.Select(x => x.Employees)
.FirstOrDefault();
OfType<T>() is one of the most important methods in EF when it comes to inheritance - you should familiarize yourself with it.
Essentially is filters the items in the query to be of a particular type - very similar to the conditional check your doing in your answer.
It's an IQueryable method (LINQ-Objects), but in LINQ-Entities (ObjectQuery<T>), it get's implemented as an INNER JOIN.
The above should work - just make sure you do the eager load after you do the OfType.
HTH.
As always, after posting the question I found this and this which has pointed me towards using projection (solution below), but I was hoping to avoid that, so I'm going to leave the question open to see if there is a more elegant solution.
var Employee = caseObjectQuery.Select(x => new{
Employee = x is CaseB ? (x as CaseB).Employee : null
}
).FirstOrDefault();
Just by selecting the Object into memory, EF magically maps the related entities.

Categories