2013-05-28 7 views
6

DISCLAIMER: jest to wklej do kopiowania ze starszego postu stackoverflow, który nie jest już dostępny, ale mam exaclty ten sam problem, więc wydawało się właściwe, aby go odświeżyć ponieważ nigdy nie otrzymał odpowiedzi.Jak korzystać z automappera do mapowania zbioru danych z wieloma tabelami

Mam procedura składowana, która zwróci 4 zestawy wyników (kontakty, adresy, e-mail, telefony), które są wypełnione w zestawie danych. Chciałbym użyć AutoMappera do wypełnienia złożonego obiektu.

public class Contact 
{ 
    public Guid ContactId { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public List<Address> Addresses { get; set; } 
    public List<Phone> Phones { get; set; } 
    public List<Email> Emails { get; set; } 
} 

public partial class Address:BaseClass 
{ 
    public Guid ContactId { get; set; } 
    public string Address1 { get; set; } 
    public string Address2 { get; set; } 
    public string Address3 { get; set; } 
    public string City { get; set; } 
    public string StateProvince { get; set; } 
    public string PostalCode { get; set; } 
    public string CountryCode { get; set; } 
} 

public class Email 
{ 
    public Guid EmailId { get; set; } 
    public Guid ContactId { get; set; } 
    public string EmailAddress { get; set; } 
} 

public class Phone 
{ 
    public Guid PhoneId { get; set; } 
    public Guid ContactId { get; set; }   
    public string Number { get; set; } 
    public string Extension { get; set; } 
} 

Mam metodę, która pobiera dane i zwraca listę kontaktów. Po zapełnieniu DataSet definiuję relacje między tabelami.

Znalazłem wiele przykładów, w których konwertujesz zestaw danych (lub tabelę) do czytnika za pomocą metody CreateDataReader i to właśnie tutaj robię. Metoda będzie w rzeczywistości analizować pierwszą tabelę w obiekcie, ale nie będzie ona wyliczana za pośrednictwem powiązanych tabel.

public List<Contact> GetContacts() 
{ 
    List<Contact> theList = null; 

    // Get the data 
    Database _db = DatabaseFactory.CreateDatabase(); 
    DataSet ds = db.ExecuteDataSet(CommandType.StoredProcedure, "GetContacts"); 

    //The dataset should contain 4 tables 
    if (ds.Tables.Count == 4) 
    {  
     //Create the maps 
     Mapper.CreateMap<IDataReader, Contact>(); // I think I'm missing something here 
     Mapper.CreateMap<IDataReader, Address>(); 
     Mapper.CreateMap<IDataReader, Email>(); 
     Mapper.CreateMap<IDataReader, Phone>(); 

     //Define the relationships   
     ds.Relations.Add("ContactAddresses", ds.Tables[0].Columns["ContactId"], ds.Tables[1].Columns["ContactId"]); 
     ds.Relations.Add("ContactEmails", ds.Tables[0].Columns["ContactId"], ds.Tables[2].Columns["ContactId"]); 
     ds.Relations.Add("ContactPhones", ds.Tables[0].Columns["ContactId"], ds.Tables[3].Columns["ContactId"]); 

     IDataReader dr = ds.CreateDataReader(); 
     theList = Mapper.Map<List<Contact>>(dr);  
    } 

    return (theList);  
} 

Mam wrażenie, że brakuje mi czegoś w odwzorowaniu obiektu Kontakt, ale po prostu nie mogę znaleźć dobrego przykładu do naśladowania.

Gdybym ręcznie wypełniają obiekt kontaktowy, a następnie przekazać to do mojego kontrolera, to właściwie załadować obiekt ContactModel pomocą bezpośredniego mapowania

public ActionResult Index() 
{ 
    //From the ContactController 
    Mapper.CreateMap<Contact, Models.ContactModel>(); 
    Mapper.CreateMap<Address, Models.AddressModel>(); 

    List<Models.ContactModel> theList = Mapper.Map<List<Contact>, List<Models.ContactModel>>(contacts); 

    return View(theList); 
} 

Czy to, co chcę robić w ogóle możliwe?

Odpowiedz

9

Mapper IDataReader jest bardzo prosty, może wypełnić obiekt z czytnika danych, gdzie mapuje właściwości obiektu według nazw kolumn. Nie został zaprojektowany do tworzenia złożonych struktur danych z relacjami, itp.

Ponadto DataSet.CreateDataReader będzie produkować wielokrotny czytnik zestawu wyników - tj. Będzie miał kilka zestawów wyników dla każdej tabeli, ale nie będzie zachowywał relacje.

Aby uzyskać to, czego chcesz, musisz utworzyć czytnik dla każdej tabeli, przypisać każdego czytnika do innej kolekcji, a następnie użyć tych wyników do utworzenia ostatecznego złożonego obiektu (obiektów).

Tutaj przedstawiam uproszczone podejście, ale możesz oszaleć, i create custom resolvers itd., Aby zamknąć wszystko.

using System; 
using System.Collections.Generic; 
using System.Data; 
using System.Linq; 
using AutoMapper; 
using NUnit.Framework; 

namespace StackOverflowExample.Automapper 
{ 
    public class Contact 
    { 
     public Guid ContactId { get; set; } 
     public string Name { get; set; } 
     public List<Address> Addresses { get; set; } 
    } 

    public partial class Address 
    { 
     public Guid AddressId { get; set; } 
     public Guid ContactId { get; set; } 
     public string StreetAddress { get; set; } 
    } 

    [TestFixture] 
    public class DatasetRelations 
    { 
     [Test] 
     public void RelationMappingTest() 
     { 
      //arrange 
      var firstContactGuid = Guid.NewGuid(); 
      var secondContactGuid = Guid.NewGuid(); 

      var addressTable = new DataTable("Addresses"); 
      addressTable.Columns.Add("AddressId"); 
      addressTable.Columns.Add("ContactId"); 
      addressTable.Columns.Add("StreetAddress"); 
      addressTable.Rows.Add(Guid.NewGuid(), firstContactGuid, "c1 a1"); 
      addressTable.Rows.Add(Guid.NewGuid(), firstContactGuid, "c1 a2"); 
      addressTable.Rows.Add(Guid.NewGuid(), secondContactGuid, "c2 a1"); 

      var contactTable = new DataTable("Contacts"); 
      contactTable.Columns.Add("ContactId"); 
      contactTable.Columns.Add("Name"); 
      contactTable.Rows.Add(firstContactGuid, "contact1"); 
      contactTable.Rows.Add(secondContactGuid, "contact2"); 

      var dataSet = new DataSet(); 
      dataSet.Tables.Add(contactTable); 
      dataSet.Tables.Add(addressTable); 

      Mapper.CreateMap<IDataReader, Address>(); 
      Mapper.CreateMap<IDataReader, Contact>().ForMember(c=>c.Addresses, opt=>opt.Ignore()); 

      //act 
      var addresses = GetDataFromDataTable<Address>(dataSet, "Addresses"); 
      var contacts = GetDataFromDataTable<Contact>(dataSet, "Contacts"); 
      foreach (var contact in contacts) 
      { 
       contact.Addresses = addresses.Where(a => a.ContactId == contact.ContactId).ToList(); 
      } 
     } 

     private IList<T> GetDataFromDataTable<T>(DataSet dataSet, string tableName) 
     { 
      var table = dataSet.Tables[tableName]; 
      using (var reader = dataSet.CreateDataReader(table)) 
      { 
       return Mapper.Map<IList<T>>(reader).ToList(); 
      } 
     } 
    } 
} 
+1

Obawiałem się, że to będzie odpowiedź, dziękuję za przykład! –

0

Jestem niesamowicie spóźniony na imprezę, ale na wypadek, gdyby to pomogło komuś innemu.

Co zrobiłem serializuje mój zestaw danych z ciągiem JSON przy użyciu Json.NET.

var datasetSerialized = JsonConvert.SerializeObject(dataset, Formatting.Indented); 

Zobacz json jako ciąg podczas debugowania w Visual Studio i skopiuj go do schowka.

Następnie w Visual Studio przejdź do Edycja -> Wklej specjalnie -> Paste JSON jako klasy

Będziesz wtedy miał poco dla każdej tabeli z relacji.

Na koniec deserializuj swój JSON do "RootObject" utworzonego podczas wklejania klas JSON As.

var rootObj = JsonConvert.DeserializeObject<RootObject>(datasetSerialized); 
Powiązane problemy