C# MVC WebAPI DTO模型与派生类绑定的最佳方法

C# MVC WebAPI DTO模型与派生类绑定的最佳方法,c#,asp.net-mvc,entity-framework,asp.net-web-api,C#,Asp.net Mvc,Entity Framework,Asp.net Web Api,针对MVC或Web Api模型绑定(尚未决定使用哪种绑定)设计DTO的最佳方法是什么,该绑定将使用实体框架(给定以下模型): public class Actor { public int ActorId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public List<ActorPhone> ActorPhones

针对MVC或Web Api模型绑定(尚未决定使用哪种绑定)设计DTO的最佳方法是什么,该绑定将使用实体框架(给定以下模型):

public class Actor
{
    public int ActorId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public List<ActorPhone> ActorPhones { get; set; }
    public List<ActorEmail> ActorEmails { get; set; }
    public List<ActorAddress> ActorAddresses { get; set; }
}

public class ActorAddress
{
    public int AddressId { get; set; }
    public int ActorId { get; set; }
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }

}

public class ActorEmail
{
    public int ActorEmailId { get; set; }
    public int ActorId { get; set; }
    public string Email { get; set; }
}

public enum ActorPhoneType
{
    HomePhone,
    WorkPhone,
    Fax
}
public abstract class ActorPhone
{
    public int ActorId { get; set; }
    public int ActorPhoneId { get; set; }
    public ActorPhoneType PhoneType { get; }
    public string Phone { get; set; }
    public ActorPhone(ActorPhoneType phoneType)
    {
        this.PhoneType = phoneType;
    }

}
public class ActorHomePhone : ActorPhone
{
    public ActorHomePhone() : base(ActorPhoneType.HomePhone) { }

}
public class ActorWorkPhone : ActorPhone
{
    public ActorWorkPhone() : base(ActorPhoneType.WorkPhone) { }

}
public class ActorFax : ActorPhone
{
    public ActorFax() : base(ActorPhoneType.Fax) { }

}
公共类参与者
{
公共int ActorId{get;set;}
公共字符串名{get;set;}
公共字符串LastName{get;set;}
公共列表ActorPhone{get;set;}
公共列表ActorEmails{get;set;}
公共列表{get;set;}
}
公务舱演员服
{
public int AddressId{get;set;}
公共int ActorId{get;set;}
公共字符串地址1{get;set;}
公共字符串地址2{get;set;}
公共字符串City{get;set;}
公共字符串状态{get;set;}
公共字符串Zip{get;set;}
}
公共类广告
{
公共int ActorEmailId{get;set;}
公共int ActorId{get;set;}
公共字符串电子邮件{get;set;}
}
公共枚举ActorPhoneType
{
家庭电话,
工作电话,
传真
}
公共抽象类ActorPhone
{
公共int ActorId{get;set;}
公共int ActorPhoneId{get;set;}
公共ActorPhoneType电话类型{get;}
公用字符串电话{get;set;}
公共ActorPhone(ActorPhone类型Phone类型)
{
this.PhoneType=PhoneType;
}
}
公共类ActorHomePhone:ActorPhone
{
公用ActorHomePhone():基(ActorPhoneType.HomePhone){}
}
公共类Actor工作电话:ActorPhone
{
public ActorWorkPhone():base(ActorPhoneType.WorkPhone){}
}
公共类ActorFax:ActorPhone
{
public ActorFax():base(ActorPhoneType.Fax){}
}
虽然地址和电子邮件可以有多个实体,而且参与者应该只有一个家庭电话、一个工作电话和一个传真

数据库有一个表ActorPhones,派生类型将从中存储

如何在MVC或WebAPI控制器中实现模型绑定。表单输入字段应命名为什么

例如,有这样一种方法:

<input name="Actor.ActorPhones[0].Phone" type="text"/>
 <input name="Actor.HomePhone.Phone" type="text"/>

这种方法:

<input name="Actor.ActorPhones[0].Phone" type="text"/>
 <input name="Actor.HomePhone.Phone" type="text"/>

还有一种方法是在Actor上将每个属性作为字符串属性公开

 public class Actor
{
    public int ActorId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string HomePhone { get; set; }
    public string WorkPhone { get; set; }
    public string Fax { get; set; }
    public List<ActorEmail> ActorEmails { get; set; }
    public List<ActorAddress> ActorAddresses { get; set; }
}
公共类参与者
{
公共int ActorId{get;set;}
公共字符串名{get;set;}
公共字符串LastName{get;set;}
公共字符串家庭电话{get;set;}
公共字符串工作电话{get;set;}
公共字符串传真{get;set;}
公共列表ActorEmails{get;set;}
公共列表{get;set;}
}
并使用:

<input name="Actor.HomePhone" type="text"/>
<input name="Actor.WorkPhone" type="text"/>
<input name="Actor.Fax" type="text"/>

使用第一个,我需要为电话类型添加一个额外的输入。第二个和第三个需要自定义逻辑将模型绑定到dbset

此外,数据库中可能有数百万参与者,出于性能原因,不应该有空电话号码。第一种和第三种方法还需要自定义逻辑来按电话类型检查ActorPhone列表,并删除或添加ActorPhone实体及其底层数据库集。例如,如果参与者在数据库中没有传真号码记录,则不应添加该记录,因此需要自定义逻辑

假设我们使用第一种方法,我们的第一个演员有一部家庭电话,只有视图可以在表单上轻松呈现以下内容,形成模型中演员电话的列表:

<input name="Actor.ActorPhones[0].Phone" type="text" required/>
<input name="Actor.ActorPhones[0].PhoneType" type="hidden" value="HomePhone"/>

将第一种方法用于具有家庭电话和工作电话的第二个参与者,以便表单具有:

<input name="Actor.ActorPhones[0].Phone" type="text" required/>
<input name="Actor.ActorPhones[0].PhoneType" type="hidden" value="HomePhone"/>
<input name="Actor.ActorPhones[0].Phone" type="text" required/>
<input name="Actor.ActorPhones[1].PhoneType" type="hidden" value="WorkPhone"/>

使用这种方法,视图的控制器可以轻松地在窗体上渲染模型

使用第二种方法,我们的视图需要查看
Actor.HomePhone
Actor.WorkPhone
Actor.Fax
导航属性是否为空,以及它们是否没有呈现相应的
。这将需要定制逻辑,可能是通过服务器端的服务,但适合使用工厂设计模式


使用第三种方法,
Actor.HomePhone
Actor.WorkPhone
Actor.Fax
作为字符串属性,视图需要检查字段是否为null,值是否不正确,从而呈现相应的
字段。此外,在服务器端还需要自定义逻辑,例如通过服务。但我不确定采用这种方法的最佳设计模式。第三种方法也没有直接在数据库中实现以节省空间。如果家庭电话、工作电话和传真添加到actors表中,并且每个都被定义为varchar(20),那么数据库将需要额外的20*3*1000000=60000000字节或每百万条记录60 mb。

我不明白您试图解决的问题。但是,如果您只允许每个类型有一个电话号码,那么您的模型将为每个
公共类ActorForm(){int HomePhone{get;set;}int WorkPhone{get;set;}int Fax{get;set;}
保留离散属性。我甚至会简单地打电话到原始类型。然后让您的控制器(或者更好的,另一个服务类)构建您的实体类型。我更新了问题,添加了您建议的方法和注意事项+谢谢你这么问。最初我不考虑这种方法,因为我试图遵循设计模式的最佳实践。第一种方法直接映射到db模型,第二种方法将自身映射到工厂模式。除了在服务逻辑中使用if语句来测试属性是否为null之外,我不确定按照这种方法使用什么设计模式,或者应用工厂模式。