使用ServiceStack.OrmLite填充POCO

时间:2013-12-13 10:02:43

标签: servicestack dapper ormlite-servicestack

考虑以下域模型:

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

客户,联系人和PhoneNumbers是我们数据库中的独立实体。

有关如何使用ORMLite和/或Dapper以最有效的方式使用所有链接的联系人和联系人电话号码填充完整客户的任何建议,同时考虑调用db和cpu周期以映射到POCO ?

2 个答案:

答案 0 :(得分:4)

如果您正在使用ORMLite,那么它有一个名为[Ignore]的属性,您可以将其放置在您的属性上,这意味着它们不会被保留(我觉得这对我的模型上有导航属性很有用)

因此,我会做以下事情:

public class Customer : IHasIntId
{
  [AutoIncrement]
  [PrimaryKey]
  public int Id {get;set}
  public string Name {get;set}
  [Ignore]
  public List<Contact> Contacts {get;set}
}

public class Contact : IHasIntId
{
  [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}
}

public class PhoneNumber : IHasIntId
{
  [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;}
}

然后在你的CustomersService中,这样的事情就足够了:

public class CustomersService : Service {
  public object Get(Customers request) {
    var customers = Db.Select<Customer>();
    var customerIds = customers.Select(c=>c.Id).ToList();
    var contacts = Db.Select<Contact>(c=>customerIds.Contains(c.CustomerId));
    var contactIds = contacts.Select(c=>c.Id).ToList();
    var phoneNumbers = Db.Select<PhoneNumber>(p=>contactIds.Contains(p.ContactId));

    customers.ForEach(customer=> {
      var customerContacts = contacts.Where(c=>c.CustomerId == customer.Id);
      customerContacts.ForEach(contact=> {
        contact.PhoneNumbers = phoneNumbers.Where(p=>p.ContactId == 
                                                       contact.Id).ToList();
      });

      customer.Contacts = customerContacts
    });

    return new CustomersResponse {
      Result = customers
    }
  }
}

注意:我正在使用ServiceStack ORMLite版本3. *。我在第4节中了解到有更好的方法,请点击此处:https://github.com/ServiceStack/ServiceStack.OrmLite/blob/master/tests/ServiceStack.OrmLite.Tests/LoadReferencesTests.cs#L80

答案 1 :(得分:4)

我已经更新了我的示例以提取扁平客户列表并将其转换为层次结构。有几点需要注意:

  • 我明确指定了连接构建器中的列,因为Id列在连接中具有相同的名称
  • 性能应该不错,因为我正在为客户使用哈希并添加电话号码,所以唯一真正的循环是搜索匹配的联系人

型号:

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 }

用法:

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

另请注意,v4似乎支持使用Reference属性更简单的方法,但它可能会对数据库进行多次调用。 See the unit tests here for an example