I'm not sure what I want is something one should expect from EF, but I think I've seen this in other ORMs and this should be quite common for it to be solved in EF - so I'm asking if it has been.
I want to be able to eagerly load foreign key entities when querying EF, but only their ids, as the other part of objects will be loaded much later.
I have:
class A {
public int Id { get; set; }
public B B { get; set; }
}
class B {
public int Id { get; set; }
// .... more properties
}
And a web API, that should return a list of all As, with this view model:
class AViewModel {
public int Id { get; set; }
public int BId { get; set; }
}
I want to make sure I do not include B table join when querying - for performance reasons. I'm also using automapper to map from A to AViewModel.
Currently the best way I found to do this is:
var a = context.As;
var aList = a.Select(x => new { model = x, bid = x.B.Id }).ToList();
return Ok(mapper.Map<List<AViewModel>(aList));
Unfortunately this means that I have to add mapping from new { model = x, bid = x.B.Id } to AViewModel, that is really unconvenient.
I'd prefer to just be able to write:
var a = context.As;
var aList = a.ToList();
return Ok(mapper.Map<List<AViewModel>(aList));
But in this case it fails with NullReferenceException, because every item of aList has B property null.
I could write context.As.Include(x => x.B) but this will join B table, that I would like to avoid.
I think I have seen some ORMs being able to fill in B objects with empty objects, except for ids - and that is the behavior I'm looking for in EF. Can it do that?
If not maybe one can suggest a nicer way of solving a problem? maybe I can fix this somehow with lazy proxies?
Put the foreignKey property on the A class:
class A {
public int Id { get; set; }
public int BId {get; set;}
[ForeignKey("BId")] //can do this in the fluent API instead
public virtual B B { get; set; }
}
Then you can just use the class A in the mapping with no need to load the B entity
Related
I'm implementing automapper for view models in a project and I'm just getting started with it. A hurdle I'm finding myself up against is a type of behavior I'm not familiar with in Automapper. I've been using automapper on a couple projects now and I haven't come across this issue before.
Basically in code this is what's happening:
var q = dbContext.Orders.Where(x => x.Name == 'name');
var l = q.ToList(); // count == 10
var shared = q.ProjectTo<OrderSharedModel>().ToList(); // count == 0
I figured out that when mapping a property that's null, it doesn't map anymore and it's like it just skips the mapping of that entity entirely.
For example:
class Order {
public int OrderId { get; set; }
public string Name { get; set; }
public int? OrderTypeId { get; set; }
public virtual OrderType { get; set; }
}
class OrderSharedModel {
public int OrderId { get; set; }
public string Name { get; set; }
public OrderTypeSharedModel OrderType { get; set; }
}
If OrderType is null in Order then it will cause ProjectTo<OrderSharedModel>().ToList() to return an empty list. If I commented out OrderType in the shared model, ProjectTo<OrderSharedModel>().ToList() will return the full list. This is weird to me.
Why would ProjectTo<OrderSharedModel>().ToList() return an empty list if the original query before ProjectTo() is not empty? Why won't automapper map a null property as null rather than skip the mapping entirely and return an empty list?
--UPDATE--
However if I use the other way mapping:
var l = q.ToList();
var sm = Mapper.Map<List<Order>, List<OrderSharedModel>>(l);
It works great. I'm doing this for now until I find exactly why the queryable extension breaks.
Just encountered the same issue, an inner property of the dto was null and for some reason it was not in the result list. I upgraded from AutoMapper 8.1 to 9.0 and it works fine now.
I have two related entities built and linked with Fluent API.
public class EDeal : IEntityBase
{
public int ID { get; set; }
public string Customer_id { get; set; }
public virtual ECustomer Customer { get; set; }
...etc
}
public class ECustomer : IEntityBase
{
public int ID { get; set; }
public string Customer_id { get; set; }
public string Customer_name { get; set; }
public virtual ICollection<EDeal> Deals { get; set; }
...etc
}
linked with
modelBuilder.Entity<ECustomer>().HasKey(c => c.Customer_id);
modelBuilder.Entity<EDeal>().HasRequired<ECustomer>(s => s.Customer)
.WithMany(r => r.Deals)
.HasForeignKey(s => s.Customer_id);
I recognize that this is inefficient linking but I had to link it in this way because I don't have control over the db structure.
The important thing to note is that the EDeal requires an ECustomer (.HasRequired). The database contains many rows in EDeal that have a null Customer_id field and I do not want to ever pull those lines when I query the entity.
I thought that the .HasRequired would make sure that I never got back any EDeals that do not have ECustomers associated with them but that doesn't seem to be the case. Instead, it only seems to ignore those lines with NULL Customer_id values when I try to order by a property in the Customer. And even then, returning the .Count() of the query behaves strangely.
var count1 = db.Set<EDeal>().Count(); //returns 1112
var count2 = db.Set<EDeal>().ToList().Count(); //returns 1112
var count3 = db.Set<EDeal>().OrderBy(c => c.Customer.Customer_name).Count(); //returns 1112
var count4 = db.Set<EDeal>().OrderBy(c => c.Customer.Customer_name).ToList().Count(); //returns 967
I know I can add a .Where(c => c.Customer.Customer_id != Null) to make sure I only get back what I'm looking for, but I'm hoping for a solution in the Entity's configuration because I have many generic functions acting on my IEntityBase class that build dynamic queries on generic Entities and I don't want to use a workaround for this case.
Questions:
1) Is there a way to limit the entity to only return those EDeals that have a corresponding ECustomer?
2) In my example above, why do count3 and count4 differ?
Thanks in advance.
I'm new working with EF and a have this scenario:
Classes:
public class Step
{
public int ID { get; set; }
public string name { get; set; }
}
public class Workflow
{
public int ID { get; set; }
public int stepID { get; set; }
public int nextStepID { get; set; }
public virtual Step Step
}
What I want to know is if have a way to get "name" from class Step based on stepID and nextStepID.
I know that I can do that
var result = (from Workflow in db.Workflow
join Step in db.Step on Workflow.stepID equals Step.ID
join nextStep in db.Step on Workflow.nextStepID equals nextStep.ID
select new
{
nameStep = Step.name,
nameNextStep = nextStep.name
}
).ToList();
but this way i'm not retrieving a Workflow entity.
I'm wondering if is possible to do something like that "automatically" using EF to retrieve a Workflow entity with Step and Next Step name.
I hope that's clear.
Thanks in advance
You can include the WorkFlow entity in the anonymous type like this:
select new
{
Workflow = Workflow,
nameStep = Step.name,
nameNextStep = nextStep.name
}
You might want to consider modeling the Workflow entity in such a way that it has two navigational properties to Step. One for the stepID foreign key and one for the nextStepID foreign key. This will make your queries simpler. Take a look at this question.
I have the following models:
public class A_DTO
{
[Key]
public string Id { get; set; }
**public virtual B_DTO B { get; set; }**
public virtual List<B_DTO> Bs { get; set; }
}
public class B_DTO
{
[Key]
public string Id { get; set; }
public string AId { get; set; }
public string UserId {get; set; }
[ForeignKey("AId"]
public virtual A_DTO A { get; set; }
[ForeignKey("UserId"]
public virtual User User { get; set; }
}
I am trying to get a list of object A_DTO but also including property B:
using AutoMapper.QueryableExtensions;
public IQueryable<A_DTO> GetAllA_DTO()
{
string userId = "8b6e9332-7c40-432e-ae95-0ac052904752";
return context.A_DTO
.Include("Bs")
.Include("B")
.Project().To<A_DTO>()
.Where(a => a.Bs.Any(b => b.UserId == userId));
}
How do I dynamically set this property according to set UserId and A_DTO.Id?
Here is a bag of observations in which you may be lucky enough to find your solution:
The B property in a code first model will result in there being a foreign key in the database table for A_DTOs that contains a reference to the B_DTOs table. Entity Framework will expect to own the responsibility for filling the B navigation property with an object populated with the data from the referenced row in the B_DTOs table, hence you would not be able to change it dynamically.
There is no need to use the Automapper Project method if your source type and destination type are the same. In your example they would both appear to be A_DTO. Are you sure you don't actually intend to have an entity "A" that is included in the context and "A_DTO" that is mapped from "A" via Automapper? If that is what you really want then you could have code in a .Select call mapping A.Bs.FirstOrDefault(b => b.UserId == userId) to A_DTO.B. However, you would not be able to apply filtering on the basis of the userId in an Automapper map.
Without seeing any of the Automapper Map setup code, it is difficult to get an idea of intent here.
As an aside, when using .Include it is better, in my opinion, to use the overload that takes an expression. In your case the includes would be rewritten:
.Include(a => a.B)
.Include(a => a.Bs)
Using this overload ensures that you will get compile time errors if you rename a property but fail to update the string in the .Include statement.
I found some questions that looked similar, but not exactly the same so I'll go for it.
I'm using EF to retrieve 2 tables. These tables/entities have no real "is a" relationship. They just happen to have a couple of common fields which I want to expose in a list containing the first N entries of a combination between the two tables. Each row has to have some way of identifying which type it is and which instance it points to.
I have solved the problem, but I guess I was wondering if there was a better way. My solution was to create a ViewModel class:
intenal class EntityAEntityBCombination
{
public int? EntityAID { get; set; }
public int? EntityBID { get; set; }
public string CommonProperty { get; set; }
}
Then I did this:
var results = (
from a in EntityAList select new EntityAEntityBCombination
{ EntityAID = a.Id, EntityBID = null, CommonProperty = a.CommonProperty }
).Concat(
from b in EntityBList select new EntityAEntityBCombination
{ EntityAID = null, EntitiyBID = b.Id, CommonProperty = b.CommonProperty }
).Fetch(N)
It works, but seems dirty. Any suggestions?
Have a look at this, perhaps it doesn't work straight out of the box, but it should give you an idea:
public class EntityA : IEntity
{}
public class EntityB : IEntity
{}
List<IEntity> results =
(from a in EntityAList select a).Cast<IEntity>()
.Concat(
(from b in EntityBList select b).Cast<IEntity>()
)
.Fetch(N).ToList();
foreach (IEntity entity in results)
{
if (entity is EntityA)
// do something with entity A
if (entity is EntityB)
// do something with entity B
}