admin管理员组文章数量:1242791
Given the following EF Core entity class:
[PrimaryKey(nameof(CustomerID))]
[Table(nameof(Customer))]
public partial class Customer
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Nullable<Int32> CustomerID { get; set; }
public string CustomerName { get; set; }
public string CustomerCode { get; set; }
public Nullable<int> HOOpAddressID { get; set; }
[ForeignKey(nameof(HOOpAddressID))]
public OperationalAddress HeadOfficeAddress { get; set; }
public string Nickname { get; set; }
public bool isActive { get; set; }
}
I have defined a DTO to hold a subset of the data as follows...
public class CustomerLookupListItem
{
public Int32 CustomerID { get; set; }
public String CustomerName { get; set; }
public String Nickname { get; set; }
public String Postcode { get; set; }
public Boolean IsActive { get; set; }
}
And, in order to use AutoMapper, I have also defined a Profile
as the Postcode
property of the CustomerLookupListItem
is not mapped from the top level:
public class CustomerLookupListItemMappingProfile : Profile
{
public CustomerLookupListItemMappingProfile()
{
CreateMap<Customer, CustomerLookupListItem>()
.ForMember(dst => dst.Postcode, opt => opt.MapFrom(src => src.HeadOfficeAddress.PostCode));
}
}
When I then use the ProjectTo<T>
method as a part of my EF query, the value of the Postcode
property in the CustomerLookupListItem
is always null.
Looking at the SQL that is sent to the database, I can see that it is not being requested as a part of the query...
info: Microsoft.EntityFrameworkCore.Database.Command
Executed DbCommand (22ms) [Parameters=[@__startsWith_0_startswith='le%' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SELECT [c].[CustomerID], [c].[CustomerName], [c].[Nickname], [c].[isActive] AS [IsActive]
FROM [Customer] AS [c]
LEFT JOIN [OperationalAddress] AS [o] ON [c].[HOOpAddressID] = [o].[OperationalAddressID]
WHERE [c].[isActive] = CAST(1 AS bit) AND [o].[PostCode] LIKE @__startsWith_0_startswith ESCAPE N'\'
ORDER BY [o].[PostCode]
I have done this (MapFrom
with ProjectTo
) elsewhere in my application in multiple places and it has worked every time so why does it not work in this instance?
Note: I have verified that the constructor of the CustomerLookupListItemMappingProfile
is being called so the mapping exists.
EDIT #1: Including the LINQ query as requested in the comments...
public async Task<List<CustomerLookupListItem>> GetCustomerPostcodeAutocompleteAsync(String startsWith, Boolean activeOnly)
=> await dbContext.Customers.AsNoTracking()
.Where(c => !activeOnly || c.isActive && c.HeadOfficeAddress.PostCode.StartsWith(startsWith))
.OrderBy(c => c.HeadOfficeAddress.PostCode)
.ProjectTo<CustomerLookupListItem>(mapper.ConfigurationProvider)
.ToListAsync();
EDIT #2: Including DebugView content from Automapper...
dbContext.Customers.ProjectTo(mapper.ConfigurationProvider).Expression.DebugView...
.Call System.Linq.Queryable.Select(
.Extension<Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression>,
'(.Lambda #Lambda1<System.Func`2[MyNameSpace.CustomerLookupListItem]>))
.Lambda #Lambda1<System.Func`2[MyNameSpace.CustomerLookupListItem]>(MyNameSpace.Customer $dtoCustomer) {
.New MyNameSpace.CustomerLookupListItem() {
CustomerID = $dtoCustomer.CustomerID ?? .New System.Int32(),
CustomerName = $dtoCustomer.CustomerName,
Nickname = $dtoCustomer.Nickname,
IsActive = $dtoCustomer.isActive
}
}
mapperConfiguration.BuildExecutionPlantypeof(Customer), typeof(CustomerLookupListItem)).Body.DebugView...
.If ($source == .Default(System.Object)) {
.If ($destination == .Default(System.Object)) {
.Default(MyNameSpace.CustomerLookupListItem)}
.Else {
$destination
}
}
.Else {
.Block(MyNameSpace.CustomerLookupListItem $typeMapDestination) {
.Block() {
$typeMapDestination = ($destination ?? .New MyNameSpace.CustomerLookupListItem());
.Try {
.Block(
System.Nullable`1[System.Int32] $resolvedValue, System.Int32 $mappedValue) {
$resolvedValue = $source.CustomerID;
$mappedValue = .If ($resolvedValue.HasValue) {
$resolvedValue.Value
}
.Else {
.Default(System.Int32)
};
$typeMapDestination.CustomerID = $mappedValue
}
}
.Catch (System.Exception $ex) {
.Throw
.Call AutoMapper.Execution.TypeMapPlanBuilder.MemberMappingError($ex, .Constant<AutoMapper.PropertyMap>(CustomerID))
};
.Try {
.Block(System.String $resolvedValue) {
$resolvedValue = $source.CustomerName;
$typeMapDestination.CustomerName = $resolvedValue
}
}
.Catch (System.Exception $ex) {
.Throw
.Call AutoMapper.Execution.TypeMapPlanBuilder.MemberMappingError($ex, .Constant<AutoMapper.PropertyMap>(CustomerName))
};
.Try {
.Block(System.String $resolvedValue) {
$resolvedValue = $source.Nickname;
$typeMapDestination.Nickname = $resolvedValue
}
}
.Catch (System.Exception $ex) {
.Throw
.Call AutoMapper.Execution.TypeMapPlanBuilder.MemberMappingError($ex, .Constant<AutoMapper.PropertyMap>(Nickname))
};
.Try {
.Block(System.Boolean $resolvedValue) {
$resolvedValue = $source.isActive;
$typeMapDestination.IsActive = $resolvedValue
}
}
.Catch (System.Exception $ex) {
.Throw
.Call AutoMapper.Execution.TypeMapPlanBuilder.MemberMappingError($ex, .Constant<AutoMapper.PropertyMap>(IsActive))
};
.Try {
.Block(System.String $resolvedValue) {
$resolvedValue = .Block(MyNameSpace.OperationalAddress $sourceHeadOfficeAddress) {
.Block() {
$sourceHeadOfficeAddress = $source.HeadOfficeAddress;
.If ($sourceHeadOfficeAddress == .Default(System.Object)) {
.Default(System.String)
}
.Else {
$sourceHeadOfficeAddress.PostCode
}
}
};
$typeMapDestination.Postcode = $resolvedValue
}
}
.Catch (System.Exception $ex) {
.Throw
.Call AutoMapper.Execution.TypeMapPlanBuilder.MemberMappingError($ex, .Constant<AutoMapper.PropertyMap>(Postcode))
};
$typeMapDestination
}
}
}
Given the following EF Core entity class:
[PrimaryKey(nameof(CustomerID))]
[Table(nameof(Customer))]
public partial class Customer
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Nullable<Int32> CustomerID { get; set; }
public string CustomerName { get; set; }
public string CustomerCode { get; set; }
public Nullable<int> HOOpAddressID { get; set; }
[ForeignKey(nameof(HOOpAddressID))]
public OperationalAddress HeadOfficeAddress { get; set; }
public string Nickname { get; set; }
public bool isActive { get; set; }
}
I have defined a DTO to hold a subset of the data as follows...
public class CustomerLookupListItem
{
public Int32 CustomerID { get; set; }
public String CustomerName { get; set; }
public String Nickname { get; set; }
public String Postcode { get; set; }
public Boolean IsActive { get; set; }
}
And, in order to use AutoMapper, I have also defined a Profile
as the Postcode
property of the CustomerLookupListItem
is not mapped from the top level:
public class CustomerLookupListItemMappingProfile : Profile
{
public CustomerLookupListItemMappingProfile()
{
CreateMap<Customer, CustomerLookupListItem>()
.ForMember(dst => dst.Postcode, opt => opt.MapFrom(src => src.HeadOfficeAddress.PostCode));
}
}
When I then use the ProjectTo<T>
method as a part of my EF query, the value of the Postcode
property in the CustomerLookupListItem
is always null.
Looking at the SQL that is sent to the database, I can see that it is not being requested as a part of the query...
info: Microsoft.EntityFrameworkCore.Database.Command
Executed DbCommand (22ms) [Parameters=[@__startsWith_0_startswith='le%' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SELECT [c].[CustomerID], [c].[CustomerName], [c].[Nickname], [c].[isActive] AS [IsActive]
FROM [Customer] AS [c]
LEFT JOIN [OperationalAddress] AS [o] ON [c].[HOOpAddressID] = [o].[OperationalAddressID]
WHERE [c].[isActive] = CAST(1 AS bit) AND [o].[PostCode] LIKE @__startsWith_0_startswith ESCAPE N'\'
ORDER BY [o].[PostCode]
I have done this (MapFrom
with ProjectTo
) elsewhere in my application in multiple places and it has worked every time so why does it not work in this instance?
Note: I have verified that the constructor of the CustomerLookupListItemMappingProfile
is being called so the mapping exists.
EDIT #1: Including the LINQ query as requested in the comments...
public async Task<List<CustomerLookupListItem>> GetCustomerPostcodeAutocompleteAsync(String startsWith, Boolean activeOnly)
=> await dbContext.Customers.AsNoTracking()
.Where(c => !activeOnly || c.isActive && c.HeadOfficeAddress.PostCode.StartsWith(startsWith))
.OrderBy(c => c.HeadOfficeAddress.PostCode)
.ProjectTo<CustomerLookupListItem>(mapper.ConfigurationProvider)
.ToListAsync();
EDIT #2: Including DebugView content from Automapper...
dbContext.Customers.ProjectTo(mapper.ConfigurationProvider).Expression.DebugView...
.Call System.Linq.Queryable.Select(
.Extension<Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression>,
'(.Lambda #Lambda1<System.Func`2[MyNameSpace.CustomerLookupListItem]>))
.Lambda #Lambda1<System.Func`2[MyNameSpace.CustomerLookupListItem]>(MyNameSpace.Customer $dtoCustomer) {
.New MyNameSpace.CustomerLookupListItem() {
CustomerID = $dtoCustomer.CustomerID ?? .New System.Int32(),
CustomerName = $dtoCustomer.CustomerName,
Nickname = $dtoCustomer.Nickname,
IsActive = $dtoCustomer.isActive
}
}
mapperConfiguration.BuildExecutionPlantypeof(Customer), typeof(CustomerLookupListItem)).Body.DebugView...
.If ($source == .Default(System.Object)) {
.If ($destination == .Default(System.Object)) {
.Default(MyNameSpace.CustomerLookupListItem)}
.Else {
$destination
}
}
.Else {
.Block(MyNameSpace.CustomerLookupListItem $typeMapDestination) {
.Block() {
$typeMapDestination = ($destination ?? .New MyNameSpace.CustomerLookupListItem());
.Try {
.Block(
System.Nullable`1[System.Int32] $resolvedValue, System.Int32 $mappedValue) {
$resolvedValue = $source.CustomerID;
$mappedValue = .If ($resolvedValue.HasValue) {
$resolvedValue.Value
}
.Else {
.Default(System.Int32)
};
$typeMapDestination.CustomerID = $mappedValue
}
}
.Catch (System.Exception $ex) {
.Throw
.Call AutoMapper.Execution.TypeMapPlanBuilder.MemberMappingError($ex, .Constant<AutoMapper.PropertyMap>(CustomerID))
};
.Try {
.Block(System.String $resolvedValue) {
$resolvedValue = $source.CustomerName;
$typeMapDestination.CustomerName = $resolvedValue
}
}
.Catch (System.Exception $ex) {
.Throw
.Call AutoMapper.Execution.TypeMapPlanBuilder.MemberMappingError($ex, .Constant<AutoMapper.PropertyMap>(CustomerName))
};
.Try {
.Block(System.String $resolvedValue) {
$resolvedValue = $source.Nickname;
$typeMapDestination.Nickname = $resolvedValue
}
}
.Catch (System.Exception $ex) {
.Throw
.Call AutoMapper.Execution.TypeMapPlanBuilder.MemberMappingError($ex, .Constant<AutoMapper.PropertyMap>(Nickname))
};
.Try {
.Block(System.Boolean $resolvedValue) {
$resolvedValue = $source.isActive;
$typeMapDestination.IsActive = $resolvedValue
}
}
.Catch (System.Exception $ex) {
.Throw
.Call AutoMapper.Execution.TypeMapPlanBuilder.MemberMappingError($ex, .Constant<AutoMapper.PropertyMap>(IsActive))
};
.Try {
.Block(System.String $resolvedValue) {
$resolvedValue = .Block(MyNameSpace.OperationalAddress $sourceHeadOfficeAddress) {
.Block() {
$sourceHeadOfficeAddress = $source.HeadOfficeAddress;
.If ($sourceHeadOfficeAddress == .Default(System.Object)) {
.Default(System.String)
}
.Else {
$sourceHeadOfficeAddress.PostCode
}
}
};
$typeMapDestination.Postcode = $resolvedValue
}
}
.Catch (System.Exception $ex) {
.Throw
.Call AutoMapper.Execution.TypeMapPlanBuilder.MemberMappingError($ex, .Constant<AutoMapper.PropertyMap>(Postcode))
};
$typeMapDestination
}
}
}
Share
Improve this question
edited 10 hours ago
Martin Robins
asked 2 days ago
Martin RobinsMartin Robins
6,16311 gold badges60 silver badges96 bronze badges
18
|
Show 13 more comments
1 Answer
Reset to default 0Summarizing from the comment discussion/debugging some possible causes for issues like this can include:
- As confirmed in your case, an additional Automapper configuration for the desired mapping overriding the desired profile mapping, which neglected the expected mapping.
- Prematurely materializing a query before projection /w a
.ToList()
/.AsEnumerable()
meaning automapper will not work custom mapping into the Query. - Namespace conflicts where the class a projection is registered to project to ends up being a different instance than the projection actually wants to write to. (Commonly caused by mis-selecting an Intellisense hint that creates a new class in a different namespace)
本文标签: entity framework coreMapFrom and ProjectTo fail to populate target propertyStack Overflow
版权声明:本文标题:entity framework core - MapFrom and ProjectTo fail to populate target property - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1740085230a2223694.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
AsQueryable
. But a test with a code first approach is not difficult. You can find examples in the AM repo. – Lucian Bargaoanu Commented 2 days agoHeadOfficeAddress.PostCode
. If EF doesn't deliver, AutoMapper can't do anything. The SQL query may very well includeOperationalAddress
only because you filter on itsPostCode
. – Gert Arnold Commented 2 days ago