C#使用外键填充嵌套类

时间:2015-02-17 21:24:42

标签: c# sql-server

我需要将两个单独的数据库调用中的信息撤回到一个对象中。

考虑以下课程

public class Status
{
    public int StatusId { get; set; }
    public string Name { get; set; }
}
public class Person
{
    public int PersonId { get; set; }
    public Status PersonStatus { get; set; }
}

这些表示两个数据库表Persons和Status,它们与Persons表中的StatusId相关。

CREATE TABLE [dbo].[Status](
    [StatusId] [INT] NOT NULL PRIMARY KEY,
    [Name] [NVARCHAR](50) NOT NULL )

CREATE TABLE [dbo].[Persons](
    [PersonId] [int] NOT NULL PRIMARY KEY,
    [StatusId] [int] NOT NULL REFERENCES dbo.Status (StatusId) )

以下是获取人员的程序:

CREATE PROCEDURE dbo.spGetPersons
AS
BEGIN
    SELECT  PersonId ,
            StatusId
    FROM    dbo.Persons
END

然后我从SqlDataReader读取数据,构造一个Person对象,并返回IList<Person>。为了填充PersonStatus属性,目前我将StatusId传递给辅助程序,该程序再次往返于数据库。

IList<Person> People = new List<Person>();
while (reader.Read())
{
    People.Add(new Person()
    {
        PersonId = (int)reader["PersonId"],
        //This causes the extra round trip to the database
        StatusId = new StatusDao().GetStatus((int)reader["StatusId"])
    });
}

StatusDao().GetStatus()只执行以下过程并构造一个Status对象。

CREATE PROCEDURE dbo.spGetStatus
    @StatusId INT = NULL
AS
    BEGIN
        SELECT  StatusId ,
                Name
        FROM    dbo.Status
        WHERE   @StatusId IS NULL
                OR @StatusId = StatusId
    END

填充PersonStatus属性的正确方法是什么?显然,我不应该为每个Person进行额外的数据库往返。我们假设我无法编辑该过程,只需将Name添加到SQL查询中。

我确实看到了使用枚举器提出的this问题。在某些情况下,这是一个不错的选择,但我们只是假设状态数据会定期更改。这使得这不再是一种选择。

我目前没有使用ORM。

我无法添加新的存储过程,也无法编辑现有的存储过程。我也不允许使用内联SQL。

1 个答案:

答案 0 :(得分:3)

由于您有一个相当奇怪的要求,即无法添加新的存储过程或使用内联SQL;无论如何,它都会去数据库两次(除非,状态是可以在枚举中捕获的东西)。

如果您有一个Status对象和一个Person对象,并且想要将它们放在一起,则可以使用以下方法:

public static List<Person> GetStatusForPerson(List<Person> people, List<Status> statuses)
{
    List<Person> peopleWithStatuses = new List<Person>();
    foreach (var p in people) 
    {
        Person person = new Person();
        person.PersonId = p.PersonId;
        person.Status = statuses.Where(s => s.StatusId == person.StatusId).FirstOrDefault());

        peopleWithStatuses.Add(person);
    }
    return peopleWithStatuses;
}

要在数据库的往返中获取此信息(对于任何遇到此问题而没有奇怪要求的人):

var persons = new List<Person>();
using (SqlConnection connection = new SqlConnection(connectionString)) {
    string sqlText = "Select p.PersonId, ps.Name, ps.StatusId FROM Person p INNER JOIN PersonStatus ps on p.StatusID = ps.StatusId WHERE p.PersonId = @personId";
    SqlCommand command = new SqlCommand();
    command.Parameters.Add("@personId", SqlDbType.Int);
    command.Parameters["@personId"].Value = personId;
    connection.Open();
    using (SqlDataReader reader = command.ExecuteReader()) {
        while (reader.Read()) 
        {
            var person = new Person();
            var status = new PersonStatus();
            person.PersonID = reader["PersonId"]; 
            status.StatusId = reader["StatusId"];
            status.Name = reader["Name"];
            person.Status = status;
            persons.Add(person);
        }
    }
}
return persons;

使用枚举

如果Status是一系列整数常量(很像enum),那么你可以简单地使用enum并存储可能的状态。

如果Status动态添加或删除了值,那么当您不使用ORM时,必须采用正常的方法来提取数据:A DataReader