4

I have two table in my database like this:

enter image description here

And i have this class:

   class Ean
   {
        public string Code{ get; set; }
   }

   class Article
   {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Ean> BarCode { get; set; }
   }

List<Article> arts = new List<Article>();

I create a list of article , and with a query using dapper. I would like to fill this list with the name of the article but also with a list of related ean Article. I try to do this query:

SELECT ART.ID AS ID, ART.NAME AS NAME,EAN.EAN AS BARCODE
FROM ART,EAN
WHERE ART.ID = EAN.ID_ART;

and in c#..

arts = conn.Query<Article>(query, null, transaction).ToList();

but don't work. How i can do? Thank's.. Any suggestion is welcome.

puti26
  • 431
  • 3
  • 14
  • 31
  • 1
    You won't be able to do that in one go. See http://stackoverflow.com/questions/7508322/how-do-i-map-lists-of-nested-objects-with-dapper?rq=1 – Alex Jun 28 '13 at 11:56
  • You should be using an inner join here, not a cross join, by the look of it. – Servy Jun 28 '13 at 18:22

3 Answers3

7

Take a look at Dapper's Multi-Mapping feature.

Dapper allows you to map a single row to multiple objects. This is a key feature if you want to avoid extraneous querying and eager load associations.

Example:

var sql = 
@"select * from #Posts p 
left join #Users u on u.Id = p.OwnerId 
Order by p.Id";

var data = connection.Query<Post, User, Post>(sql, (post, user) => { post.Owner = user; return post;});
var post = data.First();

post.Content.IsEqualTo("Sams Post1");
post.Id.IsEqualTo(1);
post.Owner.Name.IsEqualTo("Sam");
post.Owner.Id.IsEqualTo(99);

Important note Dapper assumes your Id columns are named "Id" or "id", if your primary key is different or you would like to split the wide row at point other than "Id", use the optional 'splitOn' parameter.

Void Ray
  • 9,849
  • 4
  • 33
  • 53
  • 2
    I think this is not correct answer because in this example Owner variable is a User object, not a List. How can I fill List in an object? Thing inverse this, If there is a "posts" list in "user" object, How can we fill posts list? – Savas Adar Oct 24 '19 at 09:53
  • 1
    I found correct answer. https://stackoverflow.com/a/17748734/793880 – Savas Adar Oct 24 '19 at 10:09
4

Try this, will work I think so

arts = conn.Query<Article>(query).ToList();

Also you need to change the query as below.

SELECT ART.ID AS [Id], ART.NAME AS [Name],EAN.EAN AS [BarCode] 
FROM ART,EAN
WHERE ART.ID = EAN.ID_ART;
Praveen
  • 55,303
  • 33
  • 133
  • 164
  • it's the same thing i do, but the problem is how to fill the List BarCode becouse the query ... EAN.EAN AS BARCODE don't fill it ! How i can do? – puti26 Jun 28 '13 at 10:15
  • @enzop92 I think you're getting null value in `BarCode`. – Praveen Jun 28 '13 at 10:27
  • @enzop92 Also can you please `arts = conn.Query
    (query,(articles, eans) => {articles.Article = articles; return eans;} splitOn: "Code"); ).ToList();`
    – Praveen Jun 28 '13 at 10:47
3

An other solution, with the benefice of using only one request, just two lines of code, and the possibility of chaining the join (exemple with three tables) :

  • Override Equals and GetHashCode for each domain object (this can be do automaticly with inheritance)
  • Add two extensions for affect the childrens rows to it's parent's row

Request :

var data = connection.Query<Table1, Table2, Table3, Table3>(
        @"  SELECT * FROM Table1
        LEFT JOIN Table2 ON Table1.Id = Table1Id
        LEFT JOIN Table3 ON Table2.Id = Table2Id
        WHERE Table1.Id IN @Ids",
    (t1, t2, t3) => { t2.Table1 = t1; t3.Table2 = t2; return t3; },
    param: new { Ids = new int[] { 1, 2, 3 });

var read = data.GroupBy(t => t.Table2).DoItForEachGroup(gr => gr.Key.Table3s.AddRange(gr)).Select(gr => gr.Key).
    GroupBy(t => t.Table1).DoItForEachGroup(gr => gr.Key.Table2s.AddRange(gr)).Select(gr => gr.Key);

Domain objects :

public class Table1
{
    public Table1()
    {
        Table2s = new List<Table2>();
    }

    public Guid Id { get; set; }
    public IList<Table2> Table2s { get; private set; }

    public override bool Equals(object obj)
    {
        if (obj as Table1 == null) throw new ArgumentException("obj is null or isn't a Table1", "obj");
        return this.Id == ((Table1)obj).Id;
    }

    public override int GetHashCode()
    {
        return this.Id.GetHashCode();
    }
}

public class Table2
{
    public Table2()
    {
        Table3s = new List<Table3>();
    }

    public Guid Id { get; set; }
    public Guid Table1Id
    {
        get
        {
            if (Table1 == null)
                return default(Guid);
            return Table1.Id;
        }
    }
    public IList<Table3> Table3s { get; private set; }
    public Table1 Table1 { get; set; }

    public override bool Equals(object obj)
    {
        if (obj as Table2 == null) throw new ArgumentException("obj is null or isn't a Table2", "obj");
        return this.Id == ((Table2)obj).Id;
    }

    public override int GetHashCode()
    {
        return this.Id.GetHashCode();
    }
}

public class Table3
{
    public Table3()
    {

    }

    public Guid Id { get; set; }

    public Guid Table2Id
    {
        get
        {
            if (Table2 == null)
                return default(Guid);
            return Table2.Id;
        }
    }
    public Table2 Table2 { get; set; }

    public override bool Equals(object obj)
    {
        if (obj as Table3 == null) throw new ArgumentException("obj is null or isn't a Table3", "obj");
        return this.Id == ((Table3)obj).Id;
    }

    public override int GetHashCode()
    {
        return this.Id.GetHashCode();
    }
}

Extensions :

public static class CollectionExtensions
{
    public static void AddRange<T>(this IList<T> that, IEnumerable<T> collection)
    {
        if (that == null)
            throw new ArgumentNullException("that", "that is null.");
        if (collection == null)
            throw new ArgumentNullException("collection", "collection is null.");

        if (that is List<T>)
        {
            ((List<T>)that).AddRange(collection);
            return;
        }

        foreach (T item in collection)
            that.Add(item);
    }

    public static IEnumerable<IGrouping<TKey, TElem>> DoItForEachGroup<TKey, TElem>(this IEnumerable<IGrouping<TKey, TElem>> group, Action<IGrouping<TKey, TElem>> action)
    {
        if (group == null)
            throw new ArgumentNullException("group", "group is null.");
        if (action == null)
            throw new ArgumentNullException("action", "action is null.");

        group.ToList().ForEach(gr => action(gr));
        return group;
    }
}
Xav987
  • 1,182
  • 1
  • 15
  • 17