Database 我的实体模型设计错误,还是我做错了?

Database 我的实体模型设计错误,还是我做错了?,database,entity-framework-4.1,ef-code-first,Database,Entity Framework 4.1,Ef Code First,在我的Winform应用程序中,我有供应商、客户、运输公司。他们是相似的,因为他们基本上是某种类型的联系人,但是他们在可用字段方面略有不同 例如,供应商需要有StartDate和EndDate字段。目前,尽管供应商和客户可能有多个联系人\实体,但我们不会在这些版本中这样做,但运输公司将有多个联系人\实体和地址。同时,供应商和客户需要PO地址和交货地址,以及两个电话号码以防万一 目前,在我的代码优先实体中,我有供应商、客户和运输公司,每个公司都包含一个作为联系人类型的PrimaryContact,

在我的Winform应用程序中,我有供应商、客户、运输公司。他们是相似的,因为他们基本上是某种类型的联系人,但是他们在可用字段方面略有不同

例如,供应商需要有StartDate和EndDate字段。目前,尽管供应商和客户可能有多个联系人\实体,但我们不会在这些版本中这样做,但运输公司将有多个联系人\实体和地址。同时,供应商和客户需要PO地址和交货地址,以及两个电话号码以防万一

目前,在我的代码优先实体中,我有供应商、客户和运输公司,每个公司都包含一个作为联系人类型的PrimaryContact,对于每个联系人类型,我都有一个地址和电话的ICCollection,该ICCollection依次存储一个或多个地址和电话信息。不同之处在于,运输公司除了PrimaryContact外,还将收集联系人

据我所知,即使我有自己设计DB/实体的自由,也不总是BLL中的对象正好映射下面的DB结构。 因此,我的想法是在我的BLL层,我将把数据从供应商翻译到BOSupplier,再翻译到表示层,当数据从表示层返回到DAL时,我将翻译到供应商。因为在我的演示层中,供应商看起来像:

    public class BOSupplier
    {
        // Primery key
        public int ID { get; set; }
        public string Name { get; set; }

        public string TaxNumber { get; set; }

        public bool InActive { get; set; }
        public DateTime? StartDate { get; set; }
        public DateTime? EndDate { get; set; }
        public string BankAccountNumber { get; set; }
        public string BankAccountName { get; set; }

        // Property related to Contact Table
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string EmailAddress { get; set; }
        public string SkypeName { get; set; }

        // Proterty related to Address Table
        // PO address Info
        public string POAddressLine { get; set; }     
        public string POCity { get; set; }
        public string PORegion { get; set; }
        public string POCountry { get; set; }
        public string POPostCode { get; set; }

        // Delivery AddressLine
        public string DelAddressLine { get; set; }
        public string DelCity { get; set; }
        public string DelRegion { get; set; }
        public string DelCountry { get; set; }
        public string DelPostCode { get; set; }


        // Proterties related to Phone table
        public string PhoneNumber1 { get; set; }
        public string PhoneNumber2 { get; set; }
    }
}
但在我的DAL层中,我的供应商将如下所示:

public class Supplier
{
    // Primery key
    public int ID { get; set; }
    public string Name { get; set; }

    public virtual Contact PrimaryContact { get; set; }
    public string TaxNumber { get; set; }

    public bool InActive { get; set; }
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public string BankAccountNumber { get; set; }
    public string BankAccountName { get; set; }
}
public static IEnumerable<BOSupplier> GetBOSuppliers()
{
    var suppliers = dbContext.Suppliers;
    BOSupplier currentSupplier;

    foreach (Supplier supplier in suppliers)
    {
        currentSupplier = new BOSupplier()
        {
            ID = supplier.ID,
            Name = supplier.Name,
            Code = supplier.Code,
            FirstName = supplier.PrimaryContact.FirstName,
            TaxNumber = supplier.TaxNumber
        };

        // PO Address
        Address poAddress = supplier.PrimaryContact.Addresses
            .FirstOrDefault<Address>(a => a.AddressTypeValue == (int)AddressTypes.Postal);
        if (poAddress != null)
        {
            currentSupplier.POAddressLine = poAddress.AddressLine1;
            currentSupplier.POCity = poAddress.City;
            currentSupplier.POCountry = poAddress.Country;
        }

        // Delivery Address
        Address delAddress = supplier.PrimaryContact.Addresses
            .FirstOrDefault<Address>(a => a.AddressTypeValue == (int)AddressTypes.Delivery);
        if (delAddress != null)
        {
            currentSupplier.DelAddressLine = delAddress.AddressLine1;
            currentSupplier.DelCity = delAddress.City;
            currentSupplier.DelCountry = delAddress.Country;
        }

        // ToDo: 
        // There is probably more to think about how we want map multi phone numbers into limited two phone numbers
        if (supplier.PrimaryContact.Phones.Count > 0)
        {
            foreach (Phone phone in supplier.PrimaryContact.Phones)
            {
                if (phone.PhoneType == PhoneTypes.Default)
                {
                    currentSupplier.PhoneNumber1 = phone.PhoneNumber;
                }
                else
                {
                    currentSupplier.PhoneNumber2 = phone.PhoneNumber;
                }
            }
        }

        this.boSupplierList.Add(currentSupplier);
    }
    return boSupplierList;
}
然后,当我为BLL类编写代码来管理我的中间BOSupplier对象和列表时,它实际上并没有将实体映射回DB端。似乎有很多低级代码只是为了从两个稍有不同的BOSupplier和Supplier传输/映射字段,如下所示:

public class Supplier
{
    // Primery key
    public int ID { get; set; }
    public string Name { get; set; }

    public virtual Contact PrimaryContact { get; set; }
    public string TaxNumber { get; set; }

    public bool InActive { get; set; }
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public string BankAccountNumber { get; set; }
    public string BankAccountName { get; set; }
}
public static IEnumerable<BOSupplier> GetBOSuppliers()
{
    var suppliers = dbContext.Suppliers;
    BOSupplier currentSupplier;

    foreach (Supplier supplier in suppliers)
    {
        currentSupplier = new BOSupplier()
        {
            ID = supplier.ID,
            Name = supplier.Name,
            Code = supplier.Code,
            FirstName = supplier.PrimaryContact.FirstName,
            TaxNumber = supplier.TaxNumber
        };

        // PO Address
        Address poAddress = supplier.PrimaryContact.Addresses
            .FirstOrDefault<Address>(a => a.AddressTypeValue == (int)AddressTypes.Postal);
        if (poAddress != null)
        {
            currentSupplier.POAddressLine = poAddress.AddressLine1;
            currentSupplier.POCity = poAddress.City;
            currentSupplier.POCountry = poAddress.Country;
        }

        // Delivery Address
        Address delAddress = supplier.PrimaryContact.Addresses
            .FirstOrDefault<Address>(a => a.AddressTypeValue == (int)AddressTypes.Delivery);
        if (delAddress != null)
        {
            currentSupplier.DelAddressLine = delAddress.AddressLine1;
            currentSupplier.DelCity = delAddress.City;
            currentSupplier.DelCountry = delAddress.Country;
        }

        // ToDo: 
        // There is probably more to think about how we want map multi phone numbers into limited two phone numbers
        if (supplier.PrimaryContact.Phones.Count > 0)
        {
            foreach (Phone phone in supplier.PrimaryContact.Phones)
            {
                if (phone.PhoneType == PhoneTypes.Default)
                {
                    currentSupplier.PhoneNumber1 = phone.PhoneNumber;
                }
                else
                {
                    currentSupplier.PhoneNumber2 = phone.PhoneNumber;
                }
            }
        }

        this.boSupplierList.Add(currentSupplier);
    }
    return boSupplierList;
}

我一直在想:也许我的实体模型应该更简单,或者有更好的方法来做我想做的事情?。因此,根据您的经验,请告诉我,我的实体模型过于复杂,或者我只需要一些更好的方法,从Bosupier映射到供应商或其他想法。

根据您对领域的描述,您的实体模型并不复杂。您可以使用将供应商映射到BOSupplier。下面是一个使用AutoMapper展平对象图的示例

我发现你们的采购商有问题。当您访问PrimaryContact和地址时,它使用延迟加载。为了避免多次往返数据库,您可以按如下方式加载它们

var suppliers = dbContext.Suppliers.Include(s => s.PrimaryContact.Addresses);

谢谢你的评论。是的,我同意结构没有那么复杂。但我有点不确定我是否应该花时间编写这个低级映射代码,或者更好,使用映射器为我完成这项工作。但是,另一个选项不是尝试规范化联系人表,而是使用平面模型/表。我正在编写的应用程序是一个股票跟踪系统,它比灵活的联系人列表多得多。@pstar如果这种查询很常见,您可以对数据进行反规范化。另一个选择是使用micro ORM创建一个视图和查询。如果我想同时加载PrimaryContact和SecondaryContact,该怎么办?谢谢你,这确实很简单,我自己整理好了:D,谢谢你Eranga指出AutoMapper,我看了教程,看了视频后,非常有趣。我将为我们的供应商和客户稍微平坦表现在,并期待着使用汽车制造商的惊吓承运人。