Загрузка связанных объектов в память (без ORM) - PullRequest
0 голосов
/ 13 января 2019

Я использую ADO.NET для чтения группы данных из базы данных в объекты в памяти.

Это модель моего домена:

// Question.cs
public class Question
{
    public int ID { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public IEnumerable<Tag> Tags { get; set; }
}

// Tag.cs
public class Tag 
{
    public int ID { get; set; }
    public string Name { get; set; }
}

Получив список вопросов, я хотел бы получить соответствующие теги для каждого вопроса. Я могу сделать это следующим образом:

// QuestionRepository.cs

public IList<Question> FindAll()
{
    var questions = new List<Question>();

    using (SqlConnection conn = DB.GetSqlConnection())
    {
        using (SqlCommand cmd = conn.CreateCommand())
        {
            cmd.CommandText = "select * from questions";

            SqlDataReader reader = cmd.ExecuteReader();

            while (reader.Read())
            {
                Question question = new Question();
                // Populate the question object using reader
                question.Load(reader);

                questions.Add(question);
            }
            reader.Close();
        }
     }
    return questions;
}


// Question.cs
public void Load(SqlDataReader reader)
{
    ID = int.Parse(reader["ID"].ToString());
    Title = reader["Title"].ToString();
    Description = reader["Description"].ToString();

    // Use Tag Repository to find all the tags for a particular question
    Tags = tagRepository.GetAllTagsForQuestionById(ID); 
}

    return questions;
}

// TagRepository.cs
public List<Tag> GetAllTagsForQuestionById(int id)
{
    List<Tag> tags = new List<Tag> ();
    // Build sql query to retrive the tags
    // Build the in-memory list of tags 
    return tags;
}

У меня вопрос: есть ли лучшие практики / шаблоны для извлечения связанных объектов из базы данных?

Большинство вопросов SO, с которыми я столкнулся при загрузке связанных данных, обеспечивают решение для структуры сущностей. На этот повторяющийся вопрос нет ответа.

Несмотря на то, что мой код работает, я хотел бы знать другие способы сделать то же самое. Самым близким объяснением, с которым я столкнулся, касающимся моей конкретной проблемы, был шаблон Lazy Load Мартина Фаулера, который, как я считаю, приведет к следующей реализации:

public class Question
{
    private TagRepository tagRepo = new TagRepository();
    private IList<Tag> tags;

    public int ID { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public IEnumerable<Tag> Tags {
        get
        {
            if (tags == null)
            {
                tags = tagRepo.GetAllTagsForQuestionById(ID);
            }
            return tags;
        }
    }  
}

Есть ли другие альтернативы?

1 Ответ

0 голосов
/ 13 января 2019

Если вы настаиваете на том, чтобы делать это в ADO.Net, то я бы предложил использовать небольшую хитрость с анонимными типами, LINQ и Enumerable.Range (0,0).

Во-первых, вам нужно создать список анонимного типа (или просто создать реальный класс, который сопоставляется с вашим оператором SQL)

var data = Enumerable.Range(0, 0).Select(x => new
{
    QestionId = 0,
    Title = "Question.Title",
    Description = "Question.Description",
    TagId = 0,
    Name = "Tag.Name"
}).ToList();

Далее вы выполняете запрос в базу данных ADO.Net и получаете результаты.

Ключевым моментом здесь является написание запроса, который возвращает все данные, которые вы ищете в одном запросе.

using (var conn = GetConnection())
{
    using (var cmd = conn.CreateCommand())
    {
        //Construct a valid SQL statement that joins questions to tags
        cmd.CommandText = "SELECT q.*, t.* FROM questions q JOIN tags t ON 1 = 1";

        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                data.Add(new
                {
                    QestionId = reader.IsDBNull(0) ? 0 : int.TryParse(reader.GetValue(0).ToString(), out var qId) ? qId : 0,
                    Title = reader.IsDBNull(1) ? string.Empty : reader.GetValue(1).ToString(),
                    Description = reader.IsDBNull(2) ? string.Empty : reader.GetValue(2).ToString(),
                    TagId = reader.IsDBNull(3) ? 0 : int.TryParse(reader.GetValue(3).ToString(), out var tId) ? tId : 0,
                    Name = reader.IsDBNull(4) ? string.Empty : reader.GetValue(4).ToString()
                });
            }
        }
    }
}

Теперь, когда ваш список полностью заполнен всеми строками, вам просто нужно преобразовать их обратно в искомый объект.

var questions = data.GroupBy(x => new {x.QestionId, x.Title, x.Description}).Select(y => new Question
{
    Id = y.Key.QestionId,
    Title = y.Key.Title,
    Description = y.Key.Description,
    Tags = y.Select(z => new Tag
    {
        Id = z.TagId,
        Name = z.Name
    })
}).ToList();
...