因此,我一直在阅读文档并查看相关的SO帖子,但是我似乎找不到所需的答案,这可能是因为我不清楚投影应该如何工作。
我有一个要通过OData查询的实体和DTO,如下所示:
ENTITIES
/// <summary>
/// Entity maintaining the state of a scheduled task.
/// </summary>
public class ScheduledTask : ISoftDeletable, IAuditable
{
/// <summary>
/// Gets or sets the unique identifier of the entity.
/// </summary>
[Key]
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the optional reference to a customer-specific task, which can be used
/// for qualified searches against the database. A null value here represents a "system"
/// task that is not associated with any customer (not a usual case).
/// </summary>
public Guid? CustomerId { get; set; }
/// <summary>
/// The name of the scheduled task. This is unique per customer.
/// </summary>
[Required]
[StringLength(100)]
public string Name { get; set; }
/// <summary>
/// Gets or sets an optional description of the scheduled task.
/// </summary>
public string Description { get; set; }
/// <summary>
/// All scheduled tasks are to invoke an API endpoint. Gets or sets the endpoint.
/// </summary>
[Required]
[StringLength(255)]
[DataType(DataType.Url)]
public string Url { get; set; }
/// <summary>
/// Gets or sets the HTTP Method (GET, POST, PUT, PATCH, DELETE) of the <see cref="Url"/> to invoke.
/// </summary>
[StringLength(30)]
public string Method { get; set; } = "GET";
/// <summary>
/// If the <see cref="Method"/> for the endpoint is POST, PUT, PATCH, etc, it may include a payload in the body
/// of the invocation. This property gets or sets the JSON-serialized payload that will be sent to the endpoint.
/// </summary>
public string SerializedPayload { get; set; }
/// <summary>
/// If set, includes the "On-Behalf-Of" header with the HTTP message.
/// </summary>
[StringLength(255)]
public string OnBehalfOf { get; set; }
/// <summary>
/// Gets or sets the timestamp that the endpoint is to be invoked.
/// </summary>
public DateTimeOffset ScheduledExecutionTimestamp { get; set; }
/// <summary>
/// If the Recurrence Pattern is set to something other than <see cref="RecurrencePattern.NotRecurring"/>, the task will
/// repeat execution per the described pattern, starting from the <see cref="ScheduledExecutionTimestamp"/>. Default value
/// is <see cref="RecurrencePattern.NotRecurring"/>
/// </summary>
[Required]
public RecurrencePattern RecurrencePattern { get; set; }
/// <summary>
/// Gets or sets the Enabled state of the recurring task. If the task is disabled, it will not invoke the specified
/// endpoint.
/// </summary>
[Required]
public bool IsEnabled { get; set; } = true;
public virtual IList<TaskTag> Tags { get; set; }
public DateTimeOffset? DeletedDate { get; set; }
public DateTime CreatedUtcTimestamp { get; set; }
[StringLength(255)]
public string LastModifiedBy { get; set; }
public DateTime LastModifiedUtcTimestamp { get; set; }
}
/// <summary>
/// A categorization/ organization mechanism for <see cref="ScheduledTask"/> objects. 0..n tags can be
/// associated with any scheduled task.
/// </summary>
public class TaskTag : IAuditable
{
/// <summary>
/// Unique identifier of the tag
/// </summary>
[Key]
public Guid Id { get; set; }
/// <summary>
/// Identifies the <see cref="ScheduledTask"/> ID associated with the tag
/// </summary>
[Required]
public Guid ScheduledTaskId { get; set; }
/// <summary>
/// The <see cref="ScheduledTask"/> associated with the tag
/// </summary>
[ForeignKey(nameof(ScheduledTaskId))]
public virtual ScheduledTask ScheduledTask { get; set; }
/// <summary>
/// The name of the tag. <see cref="Name"/> and <see cref="ScheduledTaskId"/> form a
/// unique combination.
/// </summary>
[Required]
[StringLength(30)]
public string Name { get; set; }
/// <summary>
/// The value of tag item, used for categorization
/// </summary>
[Required]
[StringLength(50)]
public string Value { get; set; }
/// <inheritdoc cref="IAuditable"/>
public DateTime CreatedUtcTimestamp { get; set; }
/// <inheritdoc cref="IAuditable"/>
[StringLength(255)]
public string LastModifiedBy { get; set; }
/// <inheritdoc cref="IAuditable"/>
public DateTime LastModifiedUtcTimestamp { get; set; }
}
DTO
/// <summary>
/// A Scheduled Request is a call to a service endpoint at some point in the future.
/// All requests take the form of SOA Web API endpoints to be invoked.
/// </summary>
public class ScheduledTaskModel
{
/// <summary>
/// Gets or sets the unique identifier of the scheduled task.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// If there is a customer associated with this request, (something that we would
/// like to group on), put the id value in here.
/// </summary>
public Guid? CustomerId { get; set; }
/// <summary>
/// A list of free-text metadata that can be used to categorize this scheduled
/// request.
/// </summary>
public IDictionary<string, string> Tags { get; set; } = new Dictionary<string, string>();
/// <summary>
/// The descriptive name of the scheduled request.
/// </summary>
[Required, StringLength(250, MinimumLength = 8)]
public string Name { get; set; }
/// <summary>
/// A (optional) more comprehensive description of the scheduled task than the descriptive name,
/// used for business justification or execution notes.
/// </summary>
[StringLength(2000)]
public string Description { get; set; }
/// <summary>
/// The SOA endpoint to hit, which will actually kick off the scheduled task.
/// </summary>
[Required, DataType(DataType.Url), StringLength(500)]
public string Url { get; set; }
/// <summary>
/// Specifies the HTTP method that hitting the <see cref="Url"/> endpoint will use; default is GET, but acceptable
/// values include POST, PATCH, PUT, and DELETE.
/// </summary>
[Required, StringLength(30)]
public string Method { get; set; } = "GET";
/// <summary>
/// If the <see cref="Method"/> supports a payload (POST, PATCH, PUT) and this is not null,
/// it will be included with the request.
/// </summary>
public object Payload { get; set; }
/// <summary>
/// If specified, sets the "On-Behalf-Of" header to the set value. Useful for attributing an action
/// to a specified user.
/// </summary>
[StringLength(250)]
public string OnBehalfOf { get; set; }
/// <summary>
/// Gets or sets the timestamp of when the scheduled task is to be invoked.
/// </summary>
[Required, DataType(DataType.DateTime)]
public DateTimeOffset ScheduledExecutionTimestamp { get; set; }
/// <summary>
/// Gets or sets the recurrence pattern of the scheduled task. Default is <see cref="RecurrencePattern.NotRecurring"/>.
/// If recurrence is set, <see cref="ScheduledExecutionTimestamp"/> will be ignored as the scheduled execution and instead
/// be used as a template to set the recurrence.
/// </summary>
[DataType(DataType.Text), Display(Name="", Description = "Gets or sets the recurrence pattern of the scheduled task. Default is 'notRecurring'. If recurrence is set, 'ScheduledExecutionTimestamp' will be ignored as the scheduled execution and instead be used as a template to set the recurrence.")]
public RecurrencePattern RecurrencePattern { get; set; }
/// <summary>
/// Gets or sets the enabled state of the scheduled task. Default <see cref="true"/>. A disabled task will not execute.
/// </summary>
public bool IsEnabled { get; set; } = true;
}
我已经像这样定义了类映射:
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<KeyValuePair<string, string>, TaskTag>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Key))
.ForMember(dest => dest.Value, opt => opt.MapFrom(src => src.Value));
cfg.CreateMap<TaskTag, KeyValuePair<string, string>>().ConvertUsing((tag, context) =>
new KeyValuePair<string, string>(tag.Name, tag.Value));
cfg.CreateMap<ScheduledTaskModel, ScheduledTask>().ForMember(dest => dest.SerializedPayload,
opt => opt.MapFrom(src => JsonConvert.SerializeObject(src.Payload)));
cfg.CreateMap<ScheduledTask, ScheduledTaskModel>().ForMember(dest => dest.Payload,
opt => opt.MapFrom(src => JsonConvert.DeserializeObject(src.SerializedPayload)));
});
映射似乎可以正常工作。我可以在实体和DTO之间进行映射,而不会出现问题,所有这些似乎都可以完美转换...直到我尝试从EF Core数据库上下文中投影IQueryable为止。我有一个类似于以下的服务方法:
public IQueryable<ScheduledTaskModel> GetScheduledTaskByCustomerId(Guid customerId)
{
var queryable = _dbContext.ScheduledTasks.Include(x => x.Tags)
.Where(x => x.CustomerId == customerId && x.DeletedDate == null);
return _mapper.ProjectTo<ScheduledTaskModel>(queryable);
}
当我通过ODataController
调用此函数时,出现错误“参数类型不匹配”……当然,它们不匹配,但是我认为我已经处理了……显然不,但是。
我的问题是(是)
1)如何确定哪种参数类型有问题(我假设它是KVP<string, string>
到TaskTag
还是序列化对象/从序列化对象对象,但我不知道是哪一个),然后
2)如何进行这项工作?还是不可能?