2013-05-28 14 views
6

DISCLAIMER: si tratta di una copia incolla da un vecchio post stackoverflow che non è più disponibile, ma ho lo stesso problema, quindi mi è sembrato opportuno ripubblicarlo come non ha mai risposto.Come si usa automapper per mappare un set di dati con più tabelle

Ho una procedura memorizzata che restituirà 4 set di risultati (contatti, indirizzi, e-mail, telefoni) che vengono popolati in un set di dati. Vorrei utilizzare AutoMapper per compilare un oggetto complesso.

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; } 
} 

Ho un metodo che otterrà dati e restituirà un elenco di contatti. Dopo che il DataSet è popolato, definisco le relazioni tra le tabelle.

Ho trovato molti esempi in cui si converte il DataSet (o tabella) per un lettore utilizzando il metodo CreateDataReader e questo è quello che sto facendo qui. In effetti, il metodo analizzerà la prima tabella nell'oggetto, ma non eseguirà l'enumerazione attraverso le tabelle correlate.

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);  
} 

Mi sento come se mi manca qualcosa nella mappatura per l'oggetto contatto, ma non riesco proprio a trovare un buon esempio da seguire.

Se io popolo manualmente l'oggetto contatto e poi passo è quello di mio controller, sarà correttamente caricare l'oggetto ContactModel utilizzando una mappatura diretta

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); 
} 

è quello che voglio fare anche possibile?

risposta

9

IDataReader mapper è molto semplice, può popolare un oggetto da un lettore di dati, dove mappa le proprietà dell'oggetto per nome di colonna. Non è stato progettato per creare strutture dati complesse con relazioni, ecc.

Inoltre, DataSet.CreateDataReader produrrà un lettore di dati con più risultati, ovvero il lettore avrà pochi set di risultati per ogni tabella, ma non conserverà le relazioni.

Quindi, per ottenere ciò che si desidera, è necessario creare un lettore per ogni tabella, mappare ciascun lettore a una raccolta diversa e quindi utilizzare questi risultati per creare gli oggetti complessi finali.

Qui sto fornendo l'approccio semplicistico, ma puoi scatenarti, e create custom resolvers, ecc., Per incapsulare tutto.

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

Temevo che questa sarebbe stata la risposta, grazie per l'esempio! –

0

Sono incredibilmente in ritardo alla festa, ma nel caso in cui questo aiuta a qualcun altro.

Quello che ho fatto è stato serializzare il mio set di dati in una stringa JSON utilizzando Json.NET.

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

Vedere le JSON come una stringa, mentre il debug in Visual Studio e copiare questo al vostro clipboard.

Poi in Visual Studio andare in Modifica -> Incolla speciale -> Incolla JSON come classi

Sarà quindi necessario un POCO per ogni tabella con i rapporti.

Infine, deserializzare tua JSON nella "RootObject" creato quando si è incollato il JSON come classi.

var rootObj = JsonConvert.DeserializeObject<RootObject>(datasetSerialized); 
Problemi correlati