C# C使用外键填充嵌套类

C# C使用外键填充嵌套类,c#,sql-server,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; } } 它们表示两个数据库表

我需要从两个单独的数据库调用中提取信息到一个对象中

考虑以下几类

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。为了填充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"])
    });
}
填充PersonStatus属性的正确方法是什么?显然,我不应该为每个人做额外的数据库往返。让我们假设我不能编辑该过程,而只是向SQL查询添加名称

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

我目前没有使用ORM


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

因为您有一个非常奇怪的要求,即不能添加新的存储过程或使用内联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时,您必须求助于正常的数据回拉方法:。

如果您使用的是ADO.NET,为什么不将两个表连接在一起,并在返回的过程中在DataReader中构造对象?@GeorgeStocker我正在阅读一些关于ADO.NET连接的内容,看起来这可能是一个不错的解决方案,但我不清楚如何在给定的场景中实现它。你能给我举个例子或什么让我走上正确的方向吗?@GeorgeStocker是的,我以前见过这个。我不知道它叫ADO.NET。我不允许在我们的环境中使用内联SQL。我会发布更多细节来回应你的回答。@GeorgeStocker是的,很抱歉。老实说,我认为这将是一个简单的问题,你应该使用LINQ,这是一个示例。@Lefka我也添加了一个LINQ解决方案;但你不可能在一个电话里做到这一点;你必须打你已经打过的两个电话。我只是有点怀疑它;因为林克在幕后做了我做过的事;如果你有ReSharper,它会通过重构向你展示LINQ版本。这当然会起作用,但这不是我真正想要的。我不允许执行内联SQL或编辑存储过程。我想我希望找到一种方法,分别提取人员和状态,然后将数据连接在一起。我知道我可以做到这一点,但我不确定是否有一种简单的方法可以再次使用say LINQ,而不是直接访问数据库。@Lefka,这在您最初的问题中是很重要的信息。既然这显然是您需要的信息,为什么不能添加一个新的存储过程来连接它呢?如果您只想访问数据库一次,那么必须一次将所有信息收回。不添加新存储过程的限制似乎有点奇怪。@GeorgetStocker这解决了我试图做的事情。LINQ方法是我一直在寻找的路线。然而,我最终还是说服了他们添加一个新的存储过程,所以我还是这样做了。无论如何,这对我来说是一次很好的学习经历。谢谢你的帮助。
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;