可通过Automapper进行查询:参数类型不匹配(仅在投影上?)

时间:2019-11-22 15:49:09

标签: c# odata automapper

因此,我一直在阅读文档并查看相关的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)如何进行这项工作?还是不可能?

0 个答案:

没有答案