Let's take such classes:
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public string ExtraProp { get; set; }
}
public class Parent
{
public int Id { get; set; }
public string Text { get; set; }
public Child Child { get; set; }
public string ParentExtraProp { get; set; }
}
public class ChildVo
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ParentVo
{
public int Id { get; set; }
public string Text { get; set; }
public ChildVo Child { get; set; }
}
Automapper mapping:
Mapper.CreateMap<Child, ChildVo>();
//.ForSourceMember(x => x.ExtraProp, o => o.Ignore()); //does not help
//.IgnoreAllNonExisting(); //does not help
Mapper.CreateMap<Parent, ParentVo>();
and query in Linq to Nhibernate:
var test = Session.Query<Parent>()
.Where(x => x.Id == myId)
.ProjectTo<ParentVo>()
.ToList();
ProjectTo selects only columns (properties) which are defined in ParentVo (not all properties from Parent class) - that's great. But is selects all columns (properties) from my Child class, despite the fact that they are not defined in ChildVo.
Why does Automapper ignore my nested property mapping? Is it possible to use all defined mappings during projection?
While I'm not familiar with nhibernate, the same test you've performed here works fine in Entity Framework. In EF you can see the query which has been generated before executing it - try doing this and seeing if it shows the additional column.
public class TestContext : DbContext {
public DbSet<Parent> Parents { get; set; }
}
var query = testContext.Parents.ProjectTo<ParentVo>();
Console.WriteLine(query.ToString());
This produces the following output (note no ExtraProp):
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Text] AS [Text],
CASE WHEN ([Extent2].[Id] IS NOT NULL) THEN 1 END AS [C1],
[Extent1].[Child_Id] AS [Child_Id],
[Extent2].[Name] AS [Name]
FROM [dbo].[Parents] AS [Extent1]
LEFT OUTER JOIN [dbo].[Children] AS [Extent2] ON [Extent1].[Child_Id] = [Extent2].[Id]
This makes me think that the problem is not AutoMapper specifically, but without seeing the generated query it's hard to tell.
Related
I'm implementing asp.net core project. I have 3 tables Apiapp, ApiAppHistory and EntityType. There are three fields with the names SentType, Status and Reason in ApiAppHistory and those fields are of kind Id (int type) in APIApphistory. I joined ApiApp and ApiAppHistory tables in order to get those three fields from ApiAppHistory but because they are of kind int and are unclear when showing the result to the user, I join them with EntityType table which has their related name. In the select part of my query, in addition to ApiApp fields I also need to have SentType, Status and Reason value fields.
Here below is my incomplete query:
var qq = _context.Apiapp
.Include(a => a.Api)
.Include(a => a.Application)
.Include(a => a.Data);
var t12 = (from r in qq
from b in _context.ApiAppHistory
from s in _context.EntityType
where r.LastRequest== b.Id && b.SentType == s.Id
&& b.Reason == s.Id
&& b.Status == s.Id
select new { r, s.name for Reason, s.name for
SentType ,s.name for Status});
I want in select part of my query, obtain name of the fields that I specified from the EntityType table. However, I don't know how to do it. I appreciate if someone helps me.
Here is my EntityType table:
Here are my APIAppHistory and EntityType class model:
public partial class ApiAppHistory
{
public int Id { get; set; }
public int? SentType { get; set; }
public int? Reason { get; set; }
public int? Status { get; set; }
public virtual Apiapp ApiApp { get; set; }
public virtual EntityType StatusNavigation { get; set; }
public virtual EntityType SentTypeNavigation { get; set; }
public virtual EntityType ReasonNavigation { get; set; }
}
public partial class EntityType
{
public EntityType()
{
ApiAppHistoryStatusNavigation = new HashSet<ApiAppHistory>();
ApiAppHistorySentTypeNavigation = new HashSet<ApiAppHistory>();
ApiAppHistoryReasonNavigation = new HashSet<ApiAppHistory>();
}
public int Id { get; set; }
public string Name { get; set; }
public string EntityKey { get; set; }
public virtual ICollection<ApiAppHistory> ApiAppHistoryStatusNavigation { get; set; }
public virtual ICollection<ApiAppHistory> ApiAppHistorySentTypeNavigation { get; set; }
public virtual ICollection<ApiAppHistory> ApiAppHistoryReasonNavigation { get; set; }
}
}
I have two Entity Framework Core entities:
public class JobOrder {
public int Id { get; set; }
public string JobTitle { get; set; }
public string Status { get; set; }
...
public IEnumerable<JobOrderUser> JobOrderUsers { get; set; }
}
public class JobOrderUser {
public int AppUserId { get; set; }
public AppUser User { get; set; }
public int JobOrderId { get; set; }
public JobOrder JobOrder { get; set; }
}
The second one is a join table used for a many-to-many between the JobOrder and User tables but I don't need to drill to the User table for this.
I want to get a collection of JobOrders that have an association with a specific user. In SQL, I would write something like this:
select distinct
a.*
from
JobOrders a
join JobOrderUser b on a.JobOrderID = b.JobOrderId
where
b.AppUserId = someId
How do I do that using LINQ method syntax?
If your entities are set up correctly, and their relationships are intact, you could load the JobOrderUsers while querying for a JobOrder and then filter by a user. Something like
JobOrder.Include(t => t.JobOrderUsers).Where(t => t.JobOrderUsers.Any(x => x.User.Id == SomeId));
You can also use the below query using join and Select the required columns data in a jobOrdersDto class. For that, you have to inject the _jobOrdersRepository and _jobOrderUserRepository repositories to your service where you are calling the required method.
var result = (from jobOrders in _jobOrdersRepository.GetAll()
join jobOrderUser in _jobOrderUserRepository.GetAll() on jobOrders.JobOrderID equals jobOrderUser.JobOrderId
where
(jobOrderUser.AppUserId == someId)
select new jobOrdersDto
{
}
Your Service class:
public class YourService
{
private readonly IRepository<jobOrders> _jobOrdersRepository;
private readonly IRepository<jobOrderUser> _jobOrderUserRepository;
public YourService(
IRepository<jobOrders> jobOrdersRepository, IRepository<jobOrderUser> jobOrderUserRepository)
: base()
{
_jobOrdersRepository = jobOrdersRepository;
_jobOrderUserRepository = jobOrderUserRepository;
}
}
I finally (after 4 weeks) got a result for a MVC5 project and it went well.
Now I'm trying to "limit" the number of results and it's flagging an error:
'Stoopid' is a type, which is not valid in the given context
'Student' is a type, which is not valid in the given context
Here's the model:
namespace viewModelA
{
public class Teacher
{
public int TeacherId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
public class Student
{
public int StudentId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string EnrollmentNo { get; set; }
}
public class Stoopid
{
[Key]
public int StoopID { get; set; }
public DateTime stopDt { get; set; }
}
public class ViewModel
{
public IEnumerable<Teacher> Teachers { get; set; }
public IEnumerable<Student> Students { get; set; }
public IEnumerable<Stoopid> Stoopids { get; set; }
}
}
and this is the linq query - notice that Teacher is fine, but Student and Stoopid are NOT. And they are all in the same .cs file. Am I missing something?
var result = (from t in Teacher
join s in Student on t.TeacherId equals s.StudentId
join st in Stoopid on s.StudentId equals st.StoopID
where t.TeacherId == 2
select new
{
TeacherID= t.TeacherId,
Code = t.Code,
t.Name,
s.StudentId,
sCode =s.Code,
sName=s.Name,
stopDt= st.stopDt
})
Edit: I added the relevant code to the HomeController. I also ran this thru LINQPad5 and it works fine so I don't know what's the deal
HomeController
You're referencing the class names in your Linq query, which is why it is throwing that error. You need to reference the actual List objects instead.
var result = (from t in mymodel.Teachers
join s in mymodel.Students on t.TeacherId equals s.StudentId
join st in mymodel.Stoopids on s.StudentId equals st.StoopID
where t.TeacherId == 2
select new
{
TeacherID= t.TeacherId,
Code = t.Code,
t.Name,
s.StudentId,
sCode =s.Code,
sName=s.Name,
stopDt= st.stopDt
})
I have 2 related entities in EF Core (database first design from an existing database) and having trouble loading one-many relationship - it's a webapi ASP.NET core 1.0 application
Brand Entity
[Table("tblBranding")]
public class Brand {
[Key]
[Column("brandingId")]
public int BrandId { get; set; }
[Column("BrandingActive")]
public bool Active { get; set; }
[JsonIgnore]
[Column("DeadBrand")]
public bool DeadBrand { get; set; }
[Column("BrandingSiteTitle")]
public string Name { get; set; }
//navigation properties
public virtual ICollection<Event> Events { get; set; }
}
Event entity:
[Table("tblEvents")]
public class Event
{
public int EventId { get; set; }
[Column("eventActive")]
public bool Active { get; set; }
[Column("eventName")]
public string Name { get; set; }
public DateTime EventCloseDate {get;set;}
public int PaxAllocationLimit { get; set; }
//navigation properties
[Column("BrandingId")]
public int BrandId { get; set; }
public virtual Brand Brand { get; set; }
public virtual ICollection<Session> Sessions { get; set; }
}
code from for FLUID API in OnModelCreating in DbContext:
modelBuilder.Entity<Event>()
.HasOne(e => e.Brand)
.WithMany(b => b.Events).HasForeignKey(e=>e.BrandId);
public virtual DbSet<Brand> Brands { get; set; }
public virtual DbSet<Event> Events { get; set; }
code from BrandsController:
[HttpGet]
public IActionResult Get() {
//var brands = from b in _context.Brands
// where b.Active == true
// orderby b.BrandName
// select b;
var brands = _context.Brands.Include(e => e.Events).Where(b => b.Active == true).OrderBy(b => b.Name);
return new ObjectResult(brands);
}
code from EventsController
// GET: api/values
[HttpGet("{id:int?}")]
public IActionResult Get(int? id) {
var events = from e in _context.Events
where e.Active == true
orderby e.Name
select e;
if (!events.Any()) {
return HttpNotFound();
}
if (id != null) {
events = events.Where(e => e.EventId == id).OrderBy(e => 0);
if (events.Count() == 0) { return HttpNotFound(); }
return new ObjectResult(events);
}
else {
return new ObjectResult(events);
}
}
When I try to load brands through the API, I get an exception:
Microsoft.Data.Entity.Storage.Internal.RelationalCommandBuilderFactory: Information: Executed DbCommand (80ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [t].[EventId], [t].[EventCloseDate], [t].[eventActive], [t].[BrandingId], [t].[EventId1], [t].[eventName], [t].[PaxAllocationLimit]
FROM [tblEvents] AS [t]
INNER JOIN (
SELECT DISTINCT [e].[BrandingSiteTitle], [e].[brandingId]
FROM [tblBranding] AS [e]
WHERE [e].[BrandingActive] = 1
) AS [e] ON [t].[BrandingId] = [e].[brandingId]
ORDER BY [e].[BrandingSiteTitle], [e].[brandingId]
Microsoft.Data.Entity.Query.Internal.SqlServerQueryCompilationContextFactory: Error: An exception occurred in the database while iterating the results of a query.
System.Data.SqlClient.SqlException (0x80131904): Invalid column name 'EventId1'.
Apart from this, is there a way to load related entities without using the "Includes" ? If I do not use include and use the standard LINQ query, the related entities are loaded as NULL
UPDATE
I'm now getting an invalid column error - i noticed in my previous code I hadn't used virtual on the ICollection in brand
now i can't figure out why is it generating EventId1 column in the SQL
EF 7 version is 1.0.0-rc1-final
UPDATE-2
After playing around with the code the Exception changed to circular dependency exception in code given the exact same code as above - I don't know why it was generating the invalid column name earlier (EventId1)
Answering my own question here - figured it out-
the 2 entities used here, I've used fully defined relationships in the EF 7
- however the JSON serializer doesn not like that,as this createsa circular dependancy - Brand Contains Events List, and each event also contains the parent brand property -
so the solution here was to add [JsonIgnore] attribute to relationship properties on the child
updated Events class:
[Table("tblEvents")]
public class Event
{
public int EventId { get; set; }
[Column("eventActive")]
public bool Active { get; set; }
[Column("eventName")]
public string Name { get; set; }
public DateTime EventCloseDate {get;set;}
public int PaxAllocationLimit { get; set; }
//navigation properties
[JsonIgnore]
[Column("brandingId")]
public virtual int BrandId { get; set; }
[JsonIgnore]
public virtual Brand Brand { get; set; }
//public virtual ICollection<Session> Sessions { get; set; }
}
Models:
public class User
{
[Key]
public int UserId { get; set; }
public string UserName { get; set; }
}
public class Resource
{
[Key]
public int ResourceId { get; set; }
public string ResourceName { get; set; }
public string ResourceDescription { get; set; }
}
public class UserResource
{
[Key, Column(Order=0)]
public int UserId { get; set; }
[Key, Column(Order=1)]
public int ResourceId { get; set; }
public int ResourceQuantity { get; set; }
}
I want to select "ResourceName" from Resource model and "ResourceQuantity" from UserResource model for a given "UserId". Also, once selected, do I need a brand new model to carry only those two specified columns?
Also note that UserResource model has a composite key so I am confused as to how to make the join... Is this right?
var userResources =
from r in imDB.Resources
join ur in imDB.UserResources
on r.ResourceId equals ur.ResourceId
select new { r.ResourceName, ur.ResourceQuantity };
Hence you're using Code first you can create your models are as below by using EF conventions.
public class User {
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<Resource> Resources { get; set; }
}
public class Resource {
public int Id { get; set; }
public string ResourceName { get; set; }
public int ResourceQuantity { get; set; }
public virtual ICollection<User> Users {get;set;}
}
Then EF will generate your junction table is as UsersResources.You don't need to create additional model as you did.EF will look after that.
When using POCOs with EF, if you mark your navigation properties as
virtual you can use additional EF supports like Lazy Loading. So in
general use a virtual keyword in navigation properties considered to
be a good practice.
UPDATE
You may try something like below:
Method 1 : Method based syntax
imDB.Resources.Where(r => r.Users.Any(u => u.UserId == userId))
Method 2 : Query based syntax
from r in imDB.Resources
from u in r.Users
where u.UserId == userId
select r;
I hope this will help to you.