Poblando POCO's con ServiceStack.OrmLite

dapper ormlite-servicestack servicestack

Pregunta

Considere el siguiente modelo de dominio:

class Customer
{
  int id {get;set}
  string Name {get;set}
  List<Contact> Contacts {get;set}
}

class Contact
{
  int id {get;set}
  string FullName {get;set}
  List<PhoneNumber> {get;set}
}

class PhoneNumber
{
  int id {get;set}
  PhoneType Type {get;set}
  string Number {get;set}
}

El cliente, el contacto y los números de teléfono son entidades separadas en nuestra base de datos.

¿Alguna sugerencia sobre cómo llenar un cliente completo con todos sus contactos vinculados y los números de teléfono de los contactos de la manera más eficiente utilizando ORMLite y / o Dapper, con la consideración de las llamadas a los ciclos de db y cpu para mapear a las POCO? ¿

Respuesta popular

He actualizado mi ejemplo para extraer la lista de clientes plana y transformarla en una jerarquía. Un par de cosas a anotar:

  • Especifico explícitamente columnas en el constructor de unión porque las columnas Id tienen el mismo nombre en la unión
  • el rendimiento debería ser decente porque estoy usando un hash para los clientes y agregando números de teléfono, por lo que el único bucle real es la búsqueda de un contacto que coincida

Modelos:

class CustomerComplete
{
    [BelongTo(typeof(Customer))]
    public string CustomerName { get; set; }

    [BelongTo(typeof(Customer))]
    public int CustomerId { get; set; }

    [BelongTo(typeof(Contact))]
    public int ContactId { get; set; }

    [BelongTo(typeof(Contact))]
    public string ContactFullName { get; set; }

    [BelongTo(typeof(PhoneNumber))]
    public int PhoneNumberId { get; set; }

    [BelongTo(typeof(PhoneNumber))]
    public PhoneType Type { get; set; }

    [BelongTo(typeof(PhoneNumber))]
    public string Number { get; set; }
}

class Customer
{
    public Customer()
    {
        this.Contacts = new List<Contact>();
    }

    [AutoIncrement, PrimaryKey]
    public int Id { get; set; }

    public string Name { get; set; }

    [Ignore]
    public List<Contact> Contacts { get; set; }
}

class Contact
{
    public Contact()
    {
        this.PhoneNumbers = new List<PhoneNumber>();
    }

    [AutoIncrement, PrimaryKey]
    public int Id { get; set; }

    public string FullName { get; set; }

    [References(typeof(Customer))]
    public int CustomerId { get; set; }

    [Ignore]
    public List<PhoneNumber> PhoneNumbers { get; set; }
}

class PhoneNumber
{
    [AutoIncrement, PrimaryKey]
    public int Id { get; set; }

    public PhoneType Type { get; set; }
    public string Number { get; set; }

    [References(typeof(Contact))]
    public int ContactId { get; set; }
}

enum PhoneType { None = 0 }

Uso:

db.CreateTableIfNotExists<Customer>();
db.CreateTableIfNotExists<Contact>();
db.CreateTableIfNotExists<PhoneNumber>();

db.Insert<Customer>(new Customer { Name = "Widget Co Inc" });
var customerId = (int) db.GetLastInsertId();

db.Insert<Contact>(new Contact { FullName = "John Smith", CustomerId = customerId });
var contactId = (int)db.GetLastInsertId();

db.Insert<PhoneNumber>(new PhoneNumber { ContactId = contactId, Number = "555.555.5555", Type = PhoneType.None });
db.Insert<PhoneNumber>(new PhoneNumber { ContactId = contactId, Number = "444.444.4444", Type = PhoneType.None });

db.Insert<Contact>(new Contact { FullName = "Jack Smith", CustomerId = customerId });
contactId = (int)db.GetLastInsertId();

db.Insert<PhoneNumber>(new PhoneNumber { ContactId = contactId, Number = "111.111.1111", Type = PhoneType.None });

// contact without phone number test
db.Insert<Contact>(new Contact { FullName = "Jill Smith", CustomerId = customerId });

// build join
var jn = new JoinSqlBuilder<Customer, Customer>()
    .LeftJoin<Customer, Contact>(
        customer => customer.Id, 
        contact => contact.CustomerId,
        customer => new { CustomerId = customer.Id, CustomerName = customer.Name },
        contact => new { ContactId = contact.Id, ContactFullName = contact.FullName })
    .LeftJoin<Contact, PhoneNumber>(
        contact => contact.Id, 
        phone => phone.ContactId,
        null,
        phone => new { PhoneNumberId = phone.Id, phone.Number, phone.Type });

var all = db.Select<CustomerComplete>(jn.ToSql());

var customerHash = new Dictionary<int, Customer>();

foreach (var cc in all) 
{
    if (!customerHash.ContainsKey(customerId))
        customerHash[customerId] = new Customer { Id = cc.CustomerId, Name = cc.CustomerName };

    if (cc.ContactId == 0) continue; // no contacts for this customer

    var contact = customerHash[customerId].Contacts.Where(x => x.Id == cc.ContactId).FirstOrDefault();
    if (null == contact)
    {
        contact = new Contact
        {
            CustomerId = customerId,
            Id = cc.ContactId,
            FullName = cc.ContactFullName
        };

        // add contact
        customerHash[customerId].Contacts.Add(contact);
    }

    if (cc.PhoneNumberId == 0) continue; // no phone numbers for this contact

    contact.PhoneNumbers.Add(new PhoneNumber
    {
        ContactId = cc.ContactId,
        Id = cc.PhoneNumberId,
        Number = cc.Number,
        Type = cc.Type
    });
}

// our hierarchical customer list
var customers = customerHash.ToList();

También tenga en cuenta que v4 parece ser compatible con una forma mucho más fácil de hacerlo utilizando el atributo de referencia, aunque puede realizar múltiples llamadas a la base de datos. Vea las pruebas unitarias aquí para un ejemplo .



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué