Как использовать automapper для сопоставления набора данных с несколькими таблицами
отказ от ответственности: это копия вставки из более старого сообщения stackoverflow, который больше не доступен, но у меня есть точно такая же проблема, поэтому казалось уместным перепечатать его, поскольку он никогда не отвечал.
у меня есть хранимая процедура, которая вернет 4 результирующих набора (контакты, адреса, электронная почта, телефоны), которые заполняются в набор данных. Я хотел бы использовать AutoMapper для заполнения сложного объекта.
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; }
}
у меня есть метод, который будет получать данные и вернуть список контактов. После заполнения набора данных я определяю отношения между таблицами.
Я нашел много примеров, когда вы конвертируете DataSet (или таблицу) в reader с помощью метода CreateDataReader, и это то, что я делаю здесь. Метод фактически проанализирует первую таблицу в объект, но не будет перечислять через связанные таблицы.
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);
}
Я чувствую, что мне чего-то не хватает в отображении объекта контакта, но я просто не могу найти хороший пример для подражания.
Если я вручную заполняю объект контакта, а затем передаю его моему контроллеру, он правильно загрузит объект ContactModel с помощью прямого сопоставления
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);
}
возможно ли то, что я хочу сделать?
2 ответов
IDataReader mapper очень прост, он может заполнить объект из считывателя данных, где он отображает свойства объекта по именам столбцов. Он не был предназначен для создания сложных структур данных с отношениями и т. д.
кроме того, набор данных.CreateDataReader создаст несколько resultset Data reader-т. е. у читателя будет несколько наборов результатов для каждой таблицы, но он не сохранит отношения.
Итак, чтобы получить то, что вы хотите, вам нужно создать считыватель для каждой таблицы сопоставьте каждый считыватель с другой коллекцией, а затем используйте эти результаты для создания конечных сложных объектов.
здесь я предоставляю упрощенный подход, но вы можете пойти диким, и создать пользовательские сопоставители, etc., чтобы инкапсулировать все.
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();
}
}
}
}
Я невероятно опаздываю на вечеринку, но в случае, если это поможет кому-то еще.
Я сериализовал свой набор данных в строку JSON, используя Json.NET.
var datasetSerialized = JsonConvert.SerializeObject(dataset, Formatting.Indented);
просмотр json в виде строки во время отладки в Visual Studio и скопируйте это в буфер обмена.
затем в Visual Studio перейдите к Edit - > Paste Special - > Paste JSON As Classes
затем у вас будет POCO для каждой таблицы с отношениями.
наконец, десериализуйте JSON в "RootObject", созданный при вставке JSON в качестве классов.
var rootObj = JsonConvert.DeserializeObject<RootObject>(datasetSerialized);