简体   繁体   English

存储过程返回列的值,但 FromSqlRaw 为 class 属性返回 null

[英]Stored Procedure returns value for a column but FromSqlRaw returns null for the class property

I have a class that is called by multiple methods.我有一个由多种方法调用的 class。 This class is:这个 class 是:

public class Policy : EntityObject
{
    public Guid PolicyId { get; set; }
    public Guid CustomerId { get; set; }
    public string PolicyNumber { get; set; }
    [NotMapped]
    public string TrimmedPolicyNumber { get; set; }
    public DateTime? PolicyEffectiveDate { get; set; }
    public DateTime? PolicyExpirationDate { get; set; }
    [NotMapped]
    public string PolicyType { get; set; }
    public string InsuranceCompany { get; set; }
    public string WritingCompany { get; set; }
    [NotMapped]
    public string BillMethod_PaymentPlan { get; set; }
    [NotMapped]
    public decimal? FullTermPremium { get; set; }
    [NotMapped]
    public string AccountExecutive { get; set; }
    [NotMapped]
    public string AccountRepresentative { get; set; }

    [NotMapped]
    public string PolicyLineOfBusiness { get; set; }

    [NotMapped]
    public string Status { get; set; }

Now I have the following stored proc:现在我有以下存储过程:

Create PROCEDURE [GetActivePoliciesByCustomer]
@CustomerId UNIQUEIDENTIFIER
AS
    SET NOCOUNT ON;
    Select
        c.CustId as CustomerId
        , p.PolId as PolicyId
        , p.PolNo as PolicyNumber
        , p.PolTypeLOB as [PolicyLineOfBusiness]
        , p.PolEffDate as PolicyEffectiveDate
        , p.PolExpDate as PolicyExpirationDate
        , cmp.Name as InsuranceCompany
        , wcmp.Name as WritingCompany
        , pr.Description as [Status]
    FROM
        Policies p
        (Further details have been omitted as it is not important)
GO

This stored proc returns data for all the fields listed above.此存储过程返回上面列出的所有字段的数据。 However, when I make this following call:但是,当我进行以下调用时:

public async Task<List<Policy>> GetActivePoliciesByCustomerId(Guid customerId)
{
    var activePolicies = await _context.Policy
        .FromSqlRaw<Policy>("EXEC [GetActivePoliciesByCustomer] @customerId={0}", customerId)
        .ToListAsync();

    return activePolicies;
}

During the debug session, I see that Status and PolicyLineOfBusiness is set to null.在调试 session 期间,我看到 Status 和 PolicyLineOfBusiness 设置为 null。 My suspicion is that the [NotMapped] attribute is preventing these fields from getting mapped and I have validated that.我怀疑[NotMapped]属性正在阻止这些字段被映射,我已经验证了这一点。 If I remove [NotMapped] attribute, I see that the Status and PolicyLineOfBusiness fields are populated.如果我删除[NotMapped]属性,我会看到填充了 Status 和 PolicyLineOfBusiness 字段。 However, this class (Policy) is used by another call:但是,此 class(策略)被另一个调用使用:

public async Task<Policy> GetPolicyByPolicyId(Guid id)
{
    var policyDetails = await _context.Policy
        .FromSqlRaw<Policy>("EXEC [GetPolicyDetailsByPolicyId] @policyId={0}", id)
        .ToListAsync();

    return policyDetails.FirstOrDefault();
}

The stored proc it is calling:它正在调用的存储过程:

CREATE PROCEDURE [GetPolicyDetailsByPolicyId]
@PolicyId UNIQUEIDENTIFIER
AS
    SET NOCOUNT ON;

    SELECT TOP 1
        p.PolId as PolicyId
        , p.CustId as CustomerId
        , p.PolNo as PolicyNumber
        , p.ShortPolNo as TrimmedPolicyNumber
        , p.PolEffDate as PolicyEffectiveDate
        , p.PolExpDate as PolicyExpirationDate
        , p.PolTypeLOB as PolicyType
        , c.[Name] as InsuranceCompany
        , wc.[Name] as WritingCompany
        , p.BillMethod_PaymentPlan
        , p.FullTermPremium
        , e1.[LastName] as AccountExecutive
        , CONCAT(e.FirstName, ' ', e.LastName) as AccountRepresentative 
    From
        Policies p
        (Further details have been omitted as it is not important)

If I remove the [NotMapped] attribute from the aforementioed properties (PolicyLineOfBusiness and Status) and use the previous method (GetPolicyByPolicyId) to call the above stored proc, I get an exception:如果我从前面的属性(PolicyLineOfBusiness 和 Status)中删除[NotMapped]属性并使用前面的方法(GetPolicyByPolicyId)来调用上述存储过程,我会得到一个异常:

System.InvalidOperationException: The required column 'PolicyLineOfBusiness' was not present in the results of a 'FromSql' operation.

So how do I solve the problem of making attributes optional and at the same time, they should be able to map to the fields returned by two different stored procs?那么如何解决使属性可选的问题,同时,它们应该能够 map 到两个不同存储过程返回的字段? If there's a better way, I am open for suggestions.如果有更好的方法,我愿意提供建议。 Thanks in advance.提前致谢。

For the separate store procedure you will need to have separate models as the column are different in both the store procedures.对于单独的存储过程,您需要有单独的模型,因为两个存储过程中的列不同。

Your Policies class you have both PolicyType and PolicyLineOfBusiness properties.您的Policies class 您同时拥有PolicyTypePolicyLineOfBusiness属性。

in GetActivePoliciesByCustomer procedure the column p.PolTypeLOB is aliased as PolicyLineOfBusiness but in GetPolicyDetailsByPolicyId procedure the column p.PolTypeLOB is aliased as PolicyTypeGetActivePoliciesByCustomer过程中,列p.PolTypeLOB别名为PolicyLineOfBusiness ,但在GetPolicyDetailsByPolicyId过程中,列p.PolTypeLOB别名为PolicyType

So, the origin column is the same for both properties, I'm not understanding the reason, but to avoid the exception on 2nd procedure you can modify it adding also the required output for PolicyLineOfBusiness :因此,两个属性的 origin 列是相同的,我不明白原因,但为了避免第二个过程出现异常,您可以对其进行修改,并为 PolicyLineOfBusiness 添加所需的PolicyLineOfBusiness

CREATE PROCEDURE [GetPolicyDetailsByPolicyId]
@PolicyId UNIQUEIDENTIFIER
AS
    SET NOCOUNT ON;

    SELECT TOP 1
        p.PolId as PolicyId
        , p.CustId as CustomerId
        , p.PolNo as PolicyNumber
        , p.ShortPolNo as TrimmedPolicyNumber
        , p.PolEffDate as PolicyEffectiveDate
        , p.PolExpDate as PolicyExpirationDate
        , p.PolTypeLOB as PolicyType
        , p.PolTypeLOB as PolicyLineOfBusiness /* *** ADD THIS LINE  *** */
        , c.[Name] as InsuranceCompany
        , wc.[Name] as WritingCompany
        , p.BillMethod_PaymentPlan
        , p.FullTermPremium
        , e1.[LastName] as AccountExecutive
        , CONCAT(e.FirstName, ' ', e.LastName) as AccountRepresentative 
    From
        Policies p
        (Further details have been omitted as it is not important)

This solution can help you out.这个解决方案可以帮助你。

First, we need to use inheritance.首先,我们需要使用 inheritance。

Second, we have to create a discriminator to handle the SPs and classes.其次,我们必须创建一个鉴别器来处理 SP 和类。

Finally, we set the discriminator in the stored procedures.最后,我们在存储过程中设置鉴别器。

Let's see some code.让我们看一些代码。

  1. Using your class:使用您的 class:
public class Policy : EntityObject
{
    public Guid PolicyId { get; set; }
    public Guid CustomerId { get; set; }
    public string PolicyNumber { get; set; }    
    public DateTime? PolicyEffectiveDate { get; set; }
    public DateTime? PolicyExpirationDate { get; set; }
    public string InsuranceCompany { get; set; }
    public string WritingCompany { get; set; }
}

public class CustomerPolicy : Policy
{
    public string Status { get; set; }
    public string PolicyLineOfBusiness { get; set; }        
}

public class DetailedPolicy : Policy
{    
    public string TrimmedPolicyNumber { get; set; }
    public string PolicyType { get; set; }
    public string BillMethod_PaymentPlan { get; set; }    
    public decimal? FullTermPremium { get; set; }    
    public string AccountExecutive { get; set; }
    public string AccountRepresentative { get; set; }
}
  1. Setting the dicriminator:设置鉴别器:
public class Policy : EntityObject
{
    public Guid PolicyId { get; set; }
    // ...
    [NotMapped]  
    public string discriminator { get; set; } 
}

Then, fluent API settings然后,流畅的API设置

// ...
public DbSet<Policy> Policy { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  // here you can use enum, byte, int, etc. instead of string.
  modelBuilder.Entity<Policy>()
    .HasDiscriminator<string>("discriminator")
    .HasValue<Policy>(nameof(Policy)) 
    .HasValue<CustomerPolicy>(nameof(CustomerPolicy))
    .HasValue<DetailedPolicy>(nameof(DetailedPolicy));
  // ...
}
  1. In this step we have to pass the discriminator (class name) in the stored procedures.在这一步中,我们必须在存储过程中传递鉴别器(类名)。
CREATE PROCEDURE [GetPolicyDetailsByPolicyId]
@PolicyId UNIQUEIDENTIFIER
AS
    SET NOCOUNT ON;

    SELECT TOP 1
        p.PolId as PolicyId
        , p.CustId as CustomerId
        , p.PolNo as PolicyNumber
        , p.ShortPolNo as TrimmedPolicyNumber
        , p.PolEffDate as PolicyEffectiveDate
        , p.PolExpDate as PolicyExpirationDate
        , p.PolTypeLOB as PolicyType
        , c.[Name] as InsuranceCompany
        , wc.[Name] as WritingCompany
        , p.BillMethod_PaymentPlan
        , p.FullTermPremium
        , e1.[LastName] as AccountExecutive
        , CONCAT(e.FirstName, ' ', e.LastName) as AccountRepresentative 

        // Add the discriminator here

        ,'DetailedPolicy' discriminator

    From
        Policies p
        (Further details have been omitted as it is not important)

Repeat in the another sp.在另一个 sp 中重复。

Create PROCEDURE [GetActivePoliciesByCustomer]
@CustomerId UNIQUEIDENTIFIER
AS
    SET NOCOUNT ON;
    Select
        c.CustId as CustomerId
        , p.PolId as PolicyId
        , p.PolNo as PolicyNumber
        , p.PolTypeLOB as [PolicyLineOfBusiness]
        , p.PolEffDate as PolicyEffectiveDate
        , p.PolExpDate as PolicyExpirationDate
        , cmp.Name as InsuranceCompany
        , wcmp.Name as WritingCompany
        , pr.Description as [Status]

        // Add the discriminator here

        'CustomerPolicy' discriminator

    FROM
        Policies p
        (Further details have been omitted as it is not important)
GO

Stored procedures' calls:存储过程的调用:

using Microsoft.EntityFrameworkCore;
using Microsoft.Data.SqlClient;

// ...

var activePolicies = await context.Policy
    .FromSqlRaw("exec [GetActivePoliciesByCustomer] @customerId", 
        new SqlParameter("customerId", customerId))
    .AsAsyncEnumerable();

var policyDetails = await context.Policy
    .FromSqlRaw("[GetPolicyDetailsByPolicyId] @policyId", 
        new SqlParameter("policyId", id))
    .AsAsyncEnumerable();

Note: you could need to use AsAsyncEnumerable or IQueryable to cast your class correctly.注意:您可能需要使用 AsAsyncEnumerable 或 IQueryable 来正确转换您的 class。

As PolicyLineOfBusiness property is present at your domain model.由于PolicyLineOfBusiness属性存在于您的域 model 中。 So the both SP must return that property value.因此,两个 SP 都必须返回该属性值。 If you remove the attribute(NotMapped) then your second SP will run successfully and the first SP will not get data.如果您删除属性(NotMapped),那么您的第二个 SP 将成功运行,而第一个 SP 将不会获取数据。 So it means that the PolicyLineOfBusiness does not need for the second SP.所以这意味着第二个 SP 不需要PolicyLineOfBusiness

But unfortunately, you can not do that as you write PolicyLineOfBusiness property mandatory by removing the attribute(NotMapped) at the domain Model.但不幸的是,当您通过删除域 Model 中的属性(NotMapped)来强制编写PolicyLineOfBusiness属性时,您不能这样做。 So you have to return the property from the second SP if you want to keep the domain model the same.因此,如果要保持域 model 相同,则必须从第二个 SP 返回属性。

CREATE PROCEDURE [GetPolicyDetailsByPolicyId]
@PolicyId UNIQUEIDENTIFIER
AS
    SET NOCOUNT ON;

    SELECT TOP 1
        p.PolId as PolicyId
        , p.CustId as CustomerId
        , p.PolNo as PolicyNumber
        , p.ShortPolNo as TrimmedPolicyNumber
        , p.PolEffDate as PolicyEffectiveDate
        , p.PolExpDate as PolicyExpirationDate
        , p.PolTypeLOB as PolicyType
        , c.[Name] as InsuranceCompany
        , wc.[Name] as WritingCompany
        , p.BillMethod_PaymentPlan
        , p.FullTermPremium
        , "" As PolicyLineOfBusiness --Have to add this as the domain model contain this property
        , e1.[LastName] as AccountExecutive
        , CONCAT(e.FirstName, ' ', e.LastName) as AccountRepresentative 
    From
        Policies p
        (Further details have been omitted as it is not important)

If you do not want to do this then create 2 different model for 2 SPs.如果您不想这样做,则为 2 个 SP 创建 2 个不同的 model。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM