Skip to content

Dapper isn't mapping correctly #326

@ianlee74

Description

@ianlee74

We're about ready to rip Dapper out of everything... Everything worked great when we did the initial development but over the past year we have had numerous times when for no apparent reason it just starts incorrectly mapping SQL columns to the POCO properties. It seems to most often be when there is an "Id" column and another column that contains the word "Id" (i.e. "SurveyId"). It also seems to be related to the query having a JOIN despite that no columns from the join are SELECTed. I really hope someone can help us figure out what we're doing wrong because I'd hate to stop using Dapper but this unpredictable behavior is unacceptable. Here's the current example...

[DataContract]
public class ClientSurvey
{
    [DataMember(EmitDefaultValue = false)]
    public int Id { get; set; }

    [DataMember]
    public int ClientId { get; set; }

    [DataMember(EmitDefaultValue = false)]
    public int SurveyId { get; set; }

    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public DateTime StartDate { get; set; }

    [DataMember]
    public DateTime? EndDate { get; set; }

    [DataMember]
    public bool IsPatientDefault { get; set; }

    [DataMember]
    public bool IsLocked { get; set; }

    [DataMember]
    public Survey Survey { get; set; }

    [DataMember]
    public string IntroHtml { get; set; }

    [DataMember]
    public string OutroHtml { get; set; }

    [DataMember]
    public int MaxResponses { get; set; }
}

...

public async Task<ClientSurvey> GetDefaultAsync(int clientId)
{
    // NOTE: This was actually "select cs.*" but has been spelled out for better understanding and to see if it would help with the mapping.  It did not.
    const string sql =
        @"select cs.[Id], cs.[ClientId], cs.[SurveyId], cs.[Name], cs.[StartDate], cs.[EndDate], cs.[IsPatientDefault], cs.[ClientSurveyBundleId], cs.[IsLocked], cs.[IntroHtml], cs.[OutroHtml], cs.[MaxResponses], cs.[Description]
          from dbo.ClientSurveys cs
          inner join dbo.Surveys s on s.Id = cs.SurveyId
          where cs.ClientId = @clientId
            and cs.IsPatientDefault = 1
            and isnull(cs.EndDate, '9999-1-1') > getdate() 
            and isnull(cs.StartDate, '2000-1-1') < getdate() 
            and s.IsActive = 1;";

    using (var cnn = await OpenConnectionAsync())
    {
        var surveys = (await cnn.QueryAsync<ClientSurvey>(sql, new {ClientId = clientId})).ToList();
        if (!surveys.Any())
        {
            return null;
        }
        if (surveys.Count() > 1)
        {
            throw new Exception("Multiple default surveys for client " + clientId);
        }
        var clientSurvey = surveys.First();
        clientSurvey.Survey = await SurveysData.Instance.GetAsync(clientSurvey.SurveyId, clientSurvey.ClientId);
        clientSurvey.SurveyId = 0; // Clear this so it doesn't serialize out and duplicate the data.
        await ReplaceVariables(clientSurvey);
        return clientSurvey;
    }
}

If this SQL is called in SSMS, I get back (among other things...): Id = 3, SurveyId = 2

However, if I call the function above then it assigns: clientSurvey.Id = 2, clientSurvey.SurveyId = 2
(note that there is not a record in the database where ClientSurvey.Id = 2)

So far we've managed to do weird things like changing or removing JOINs in exchange for less desirable queries just to force Dapper to get the mapping correct but this now seems to be an almost daily problem. Is anyone else experiencing this?

Our databases are Azure Database (v12) and our code is running in an Azure Website running .NET 4.5.2.

Thanks for any help you can offer. This is an urgent problem for us.

Ian

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions