Лучший способ извлечь один из многих отношений с dapper dot net orm?

У меня есть два класса "Продукт" и "Продавец".

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }

    public Seller Seller { get; set; }
    public int? SellerId { get; set; }
}
public class Seller
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Product> Products { get; set; }
}

Я хочу извлечь список продавцов со всеми их продуктами, используя dapper.

В настоящее время я делаю так:

Dictionary<int, Seller> dic = new Dictionary<int, Seller>();
        Conn.Query<Seller, Product, int>
            (@"select s.*,p.* from Sellers s Join Products p 
                on p.SellerId = s.Id",
            (s, p) => {
                if (dic.ContainsKey(s.Id))
                    dic[s.Id].Products.Add(p);
                else
                {
                    s.Products = new List<Product>();
                    s.Products.Add(p);
                    dic.Add(s.Id, s);
                }
                return s.Id; 
            });
        var sellers = dic.Select(pair => pair.Value);

Есть ли лучший способ?

Ответ 1

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

//Normalized Database classes
public class Seller
{
    public int Id { get; set; }  // primary key
    public string Name { get; set; }
}

public class Product
{
    public int Id { get; set; }  // primary key
    public int SellerId { get; set; } // foreign key
    public string Name { get; set; }
    public decimal Price { get; set; }
}

Затем вы можете напрямую запросить таблицы Продавца и продукта.

var Sellers = connection.Query<Seller>("Select * from Seller");
var Products = connection.Query<Product>("Select * from Product");

Затем используйте linq "group by", чтобы загрузить Product в словарь

var SellerWithProductsDict = 
        (from prod
        in Products 
        group prod by prod.SellerId
        into groupedProducts
        select groupedProducts)
        .ToDictionary(gp => gp.SellerId, gp => gp.ToList());

Затем вы можете пройти через SalesWithProductsDict, чтобы просмотреть все продукты продавца, и если вам нужно имя продавца, просто получите его по индексу из результата запроса продавцов.

**************************************

В конце ответа, но если вам действительно нужны продукты, смешанные с продавцами, вы можете использовать одну и ту же структуру базы данных выше и сделать что-то вроде:

var qry = @"Select s.Id as SellerId,
                   s.Name as SellerName,
                   p.Id as ProductId,
                   p.Name as ProductName,
                   p.Price as ProductPrice
            From Seller as s, Product as p
            Where s.Id = p.id"

var SellerWithProducts = connection.Query(qry)

Затем используйте функции linq "group by", чтобы перебросить их в словарь. Я бы предложил посмотреть эту "группу Linq - по нескольким полям" сообщение для помощи с группой по linq

Ответ 2

Мой последний пост по какой-то причине сходил с ума.

Во-первых, может быть плохой идеей получить все от вашей базы данных. Возможно, вы захотите ограничить свой запрос. Несмотря на то, что теперь ваш стол может быть небольшим, вы не хотите сопротивляться стене.

С учетом сказанного я бы рекомендовал упростить код для получения Продавцов и Продуктов, добавив статические методы к Продавцу и Продукту.

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }

    public int? SellerId { get; set; }
    public Seller Seller { get; set; }

    public static Product GetProductById(int id)
    {
        Product product = null;
        using (SqlConnection connection = new SqlConnection("connectionString"))
        {
            product = connection.Query<Product>("select * from Product where Id = @Id", new { Id = id }).SingleOrDefault();
            product.Seller = connection.Query<Seller>("select * from Seller where Id = @Id", new { Id = product.SellerId }).SingleOrDefault();
        }

        return product;
    }
}

public class Seller
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Product> Products { get; set; }

    public static Seller GetSellerById(int id)
    {
        Seller seller = null;
        using (SqlConnection connection = new SqlConnection("connectionString"))
        {
            seller = connection.Query<Seller>("select * from Seller where Id = @Id", new { Id = id }).SingleOrDefault();
            if(seller != null)
            {
                seller.Products = connection.Query<Product>("select * from Product where SellerId = @Id", new { Id = id }).ToList();
                seller.Products.ForEach(p => p.Seller = seller);
            }
        }

        return seller;
    }
}

Имейте в виду, что это грубо и не представляет все, что вам может понадобиться, но оно легче использовать в долгосрочной перспективе и следует за объектно-ориентированным дизайном лучше, чем сделать один из запросов для получения ваших данных.

Вы можете расширить эту идею, добавив методы, которые запрашивают другие вещи о продавце или продукте, таком как GetSellersInZipCode (int zipCode) и возвращают List с помощью этого метода.