与 Dapper、.NET Core 和 Postgres 的映射不正确

Incorrect mapping with Dapper, .NET Core and Postgres

提前致谢。我发现我使用 Postgres 和 Dapper 的映射存在问题。我正在尝试映射一个 Organization 对象,它与 Location 对象具有一对多关系。以下是我的映射代码:

public Organization GetOrganizationById(long id)
        {
            var query = @"SELECT 
                 o.organization_id,
                 o.guid,
                 o.name,
                 o.is_active,
                 o.created,
                 l.location_id,
                 l.org_id,
                 l.guid,
                 l.internal_identifier,
                 l.name,
                 l.address,
                 l.city,
                 l.state,
                 l.zip,
                 l.phonenumber,
                 l.is_active,
                 l.opendate,
                 l.closedate,
                 l.created
                FROM organization AS o
                 INNER JOIN location as l 
                    ON o.organization_id = l.org_id
                 WHERE o.organization_id = @Id";

            using (var con = new NpgsqlConnection(_connectionString))
            {

                var orgResult = con.Query<Organization, List<Location>, Organization>(
                    query,
                    (org, locations) =>
                    {
                        org.Locations = locations;
                        return org;
                    },
                    new {Id = id},
                    splitOn: "location_id").FirstOrDefault();
                
                
                return orgResult;
            }
        }

我创建了以下对象:

    public class Organization
    {
        public long OrganizationId { get; set; }
        public Guid Guid { get;set; }
        public string Name { get; set; }
        public bool IsActive { get; set; }
        public DateTime Created { get; set; }
        
        //[JsonIgnore]
        public List<Location> Locations { get; set; }
        
    }

    public class Location
    {
        public long LocationId { get; set; }
        public long OrgId { get; set; }
        public Guid Guid { get; set; }
        public string InternalIdentifier { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Zip { get; set; }
        public string PhoneNumber { get; set; }
        public bool IsActive { get; set; }
        public DateTime OpenDate { get; set; }
        public DateTime? CloseDate { get; set; }
        public DateTime Created { get; set; }
        
        [JsonIgnore]
        public Organization Organization { get; set; }

    }

现在的问题是,当我得到结果时,它甚至都不准确。虽然查询在复制到 SQL 客户端时返回了正确的结果。

这是我在 JSON 回复中看到的内容以及不正确的内容:

{
  "organizationId": 0, // This is incorrect. The organization ID is one (this appears to be the default int/long)
  "guid": "4fc55437-8497-4388-be48-c6b8c5dfee93", // This is correct
  "name": "TestOrg", // This is correct
  "isActive": false, // This is incorrect. Both locations are active, as is the organization
  "created": "2021-01-27T05:20:42.287925", // This is correct 
  "locations": [] // Incorrect, there should be two locations present
}

无论如何,您认为我在映射中遗漏了什么可以防止这些记录无法正确映射到它们的 POCO 吗?

Dapper 要求您的模型 属性 名称反映您的 table(不区分大小写)。

  • 您可以尝试使用属性上方的 [ColumnName()] 属性。我听说这对某些人有用,但对我没有用。我最终使用了 AutoMapper 并创建了一个映射器对象。
[ColumnName("SomeFieldName")]
public string SomeProperty { get; set; }

在你研究之前(因为它很痛苦)尝试使用 Dapper 的 QueryMultiple() 方法。

Dapper's Query Multiple

您可能需要尝试一下。自从我以 Dapper 方式编写完整的 QueryMultiple() 方法以来已经有一段时间了。我制作了一个包装器 class 来容器化我的 dapper 函数(我只是将所有第 3 方包容器化,以防出现异常或其他情况)。这是我使用 QueryMultiple(). 到 return 2 个数据集的方法之一,使用 out 参数。您可以根据需要将其调整为 return。


/// <summary>
/// Executes a query with multiple results and stores the result sets in the out parameters.
/// </summary>
/// <typeparam name="T1">The type of the first result set.</typeparam>
/// <typeparam name="T2">The type of the second result set.</typeparam>
/// <typeparam name="P">The parameter type. Generally an autonomous/dynamic object, a <see cref="DynamicParameters"/>, or an <see cref="ExpandoObject"/>.</typeparam>
/// <param name="sql">The SQL query string or stored procedure.</param>
/// <param name="parameters">The parameter(s) for the stored procedure.</param>
/// <param name="results1">The first result set.</param>
/// <param name="results2">The second result set.</param>
/// <param name="queryType">
/// <para>The query's command type.</para>
/// Defaults to <strong><see cref="CommandType.StoredProcedure"/></strong>.
/// </param>
public void ExecuteQueryMultiple<T1, T2, P>(string sql, P parameters, 
    ConnStringKey connectionKey, // This is a personal app setting enum...
    out List<T1> results1, out List<T2> results2, 
    CommandType queryType = CommandType.StoredProcedure)
{
    using (IDbConnection connection = new 
        SqlConnection(configurations.GetConnectionString(connectionKey)))
    {
        using (SqlMapper.GridReader sqlReader = connection.QueryMultiple(sql, 
            parameters, commandType: queryType, commandTimeout: ConnectionTimeout))
        {
            results1 = sqlReader.Read<T1>().AsList();
            results2 = sqlReader.Read<T2>().AsList();
        }
    }
}

如果您的 SQL 是这样的,您可以使用类似的东西:

SELECT 
    o.organization_id,
    o.guid,
    o.name,
    o.is_active,
    o.created,
FROM organization AS o
WHERE o.organization_id  = @Id

SELECT
    l.location_id,
    l.org_id,
    l.guid,
    l.internal_identifier,
    l.name,
    l.address,
    l.city,
    l.state,
    l.zip,
    l.phonenumber,
    l.is_active,
    l.opendate,
    l.closedate,
    l.created
FROM location AS l
WHERE l.org_id = @Id  

当然,那你得在代码中把两个结果集聚合起来。


用法:


var someParams = new { Id = someId };

sql = "your sql";

_sql.ExecuteQueryMultiple<Organization, Location, dynamic>(
    sql, someParams, 
    // this is an enum of mine that ties back to my appsettings.json or app.config file to get the connection string
    MyConfigurationConnectionKey, 
    out List<Organization> organizations, 
    out List<Location> locations, 
    CommandType.Text);

// Aggregate the two data sets...
organizations.Locations = locations; // or however you need to do it