在AutoMapper和Entity Framework中进行算术运算

时间:2013-08-14 14:34:04

标签: c# entity-framework exception automapper

我将POCO对象投影到DTO对象时遇到了一些困难

我的POCO对象

public class LogEntry
{
    public LogEntry()
    {
        this.Username = Environment.UserName;
        this.LogNote = String.Empty;

        var now = DateTime.Now;
        this.LoginTime = now;
        this.LastSeenInput = now;
        this.ServerName = Environment.MachineName;
    }

    public int LogEntryId { get; set; }

    [Required(AllowEmptyStrings = false)]
    [Column(TypeName = "nvarchar")]
    [MaxLength(64)]
    public string Username { get; set; }

    [Required(AllowEmptyStrings = true)]
    [Column(TypeName = "varchar")]
    [MaxLength(20)]
    public string ServerName { get; set; }

    public DateTime LoginTime { get; set; }
    public TimeSpan ConnectedFor { get; set; }
    public TimeSpan IdleFor { get; set; }
    public DateTime? LastSeenInput { get; set; }

    [Required(AllowEmptyStrings = true)]
    public string LogNote { get; set; }
}

我的DTO对象

public sealed class ClientEventDTO
{
    public int LogEntryId { get; set; }
    public string Username { get; set; }
    public string ServerName { get; set; }
    public DateTime LoginTime { get; set; }
    public TimeSpan ConnectedFor { get; set; }
    public string LogNote { get; set; }

    public TimeSpan IdleForWithLast { get; set; }
}

我想要做的是我希望IdleForWithLast代表

public TimeSpan IdleForWithLast
{
    get
    {
        var tmp = IdleFor;
        if (LastSeenInput.HasValue)
            tmp = tmp.Add(DateTime.Now - LastSeenInput.Value);
        return tmp;
    }
}

但是当我这样做时

static void Main(string[] args)
{
    Mapper.CreateMap<LogEntry, ClientEventDTO>()
        .ForMember(dto => dto.IdleFor, 
                   a => a.MapFrom(ent => ent.LastSeenInput == null ? 
                                            ent.IdleFor : 
                                            ent.IdleFor + (DateTime.Now - ent.LastSeenInput.Value)
                                 )
                  );

    Mapper.AssertConfigurationIsValid();

    using (var ctx = new LoginHistoryContext())
    {
        var username = "Scott";

        var query = ctx.LogEntries.Where(entry => entry.Username == username).Project().To<ClientEventDTO>();
        var result = query.ToList();

        Debugger.Break();
    }
}

我收到以下异常

  

System.ArgumentException未处理
  的HResult = -2147024809
  Message = DbArithmeticExpression参数必须具有数字公共类型   源= System.Data.Entity的

ent.IdleFor + (DateTime.Now - ent.LastSeenInput.Value)更改为ent.IdleFor.Add(DateTime.Now - ent.LastSeenInput.Value)只会给我一个不同的例外

  

System.NotSupportedException未处理
  的HResult = -2146233067
  Message = LINQ to Entities无法识别方法'System.TimeSpan Add(System.TimeSpan)'方法,并且此方法无法转换为商店表达式。
  源= System.Data.Entity的

在保持兼容性QueryableExtensions时使用这种操作的正确方法是什么?

如果您不关心兼容性,可以通过这种方式轻松完成

static void Main(string[] args)
{
    Mapper.CreateMap<LogEntry, ClientEventDTO>()
        .ForMember(dto => dto.IdleFor, opt=>opt.ResolveUsing<CustomResolver>());

    Mapper.AssertConfigurationIsValid();

    using (var ctx = new LoginHistoryContext())
    {
        var username = "Scott";

        var query = ctx.LogEntries.Where(entry => entry.Username == username);
        var result = Mapper.Map<List<ClientEventDTO>>(query.ToList());

        Debugger.Break();
    }
}

public class CustomResolver : ValueResolver<LogEntry, TimeSpan>
{
    protected override TimeSpan ResolveCore(LogEntry source)
    {
        var tmp = source.IdleFor;
        if (source.LastSeenInput.HasValue)
            tmp = tmp.Add(DateTime.Now - source.LastSeenInput.Value);
        return tmp;
    }
}

这就是我可能会在这种情况下使用的工作,但我想知道将来如何处理这个问题,以防我必须使用QueryableExtensions并且我需要做类似的事情。

1 个答案:

答案 0 :(得分:2)

您可以忽略此成员,并在映射后手动映射:

Mapper.CreateMap<LogEntry, ClientEventDTO>()
      .ForMember(dto => dto.IdleForWithLast, m => m.Ignore())
      .AfterMap((ent, dto) =>
           dto.IdleForWithLast = ent.LastSeenInput.HasValue ?
           ent.IdleFor + (DateTime.Now - ent.LastSeenInput.Value) : ent.IdleFor);

或者在地图后更改默认映射:

Mapper.CreateMap<LogEntry, ClientEventDTO>()
      .ForMember(dto => dto.IdleForWithLast, m => m.MapFrom(ent => ent.IdleFor))
      .AfterMap((ent, dto) => {
            if (ent.LastSeenInput.HasValue)
                dto.IdleForWithLast += DateTime.Now - ent.LastSeenInput.Value;
       });