如何设计包含查找表中属性的类

本文关键字:属性 查找 包含 | 更新日期: 2023-09-27 18:28:05

一些上下文:这与我构建多层web应用程序的愿望有关:

  1. C#ASP.NET web窗体
  2. C#POCO业务对象
  3. 某种DAL。。。SQL(或者EF4,如果我能弄清楚的话)

例如,我真的不想要一个让我的表示层直接与EF实体对话的响应

我一直在用C#进行自己的web开发;SQL已经10年了,但在正式OOAD方面,我还是一个新手。我最近一直在满怀激情地追求这项技能,但我还是新手,有些事情我无法完全理解。我希望有人能用一种能带来顿悟的方式来解释它:

假设我创建了一个web应用程序,以某种方式管理People,并且我的Person对象必须具有FirstName、LastName、HairColor、EyeColor、Ethnicity、StateOrProvince等属性。我使用SQL Server来实现持久性。。。因此,常识会规定People表中的各个字段都是外键:

FirstName varchar(50)
LastName varchar(50)
HairColor tinyint
EyeColor tinyint
Ethnicity tinyint
StateOrProvince tinyint

很明显,这意味着我为每个字段都有相应的查找表(即HairColors表、EyeColors表和Ethnicity表等),每个查找表都有一个ID和一个名称。当然,每当我想显示任何关于Person的有用信息时,这些查找表中的Name字段都将与我的People数据进行JOINed。

该网站的一些关键功能是:

1.)枚举网格视图中的人员(名字、姓氏、头发颜色、眼睛颜色、种族、州或省)

2.)在只读页面上显示个人详细信息(名字、姓氏、头发颜色、眼睛颜色、种族、州或省)

3.)允许用户在更新页面上更新个人数据(名字、姓氏、头发颜色、眼睛颜色、种族、州或省)

案例#1如果我在网格视图中枚举Person对象的集合。。。每个Person实例都需要将其HairColor、EyeColor、Ethnicity、StateOrProvince属性显示为字符串才能有意义(即SQL查找表中的Name字段,而不是其ID)。很明显,我的SQL存储过程会有一些JOIN,为我提供在每个Person实例中填充这些文本属性所需的适当字符串数据。

案例#2同样,我的存储过程将有一个JOIN,以字符串的形式返回人类可读的属性名称,并且我将使用myPerson.HearColor、myPerson.EyeColor等在只读标签控件中显示它们。

案例#3在这里,我将显示一个页面,其中包含每个属性的下拉列表(即value=HairColorId,Text=HairColorName)。我的直觉是使用每个属性的ID(类似于myPerson.HearColorId)来循环DDL项,并选择一个值,该值表示People表当前为此人保留的头发颜色。如果用户在任何属性DDL中选择了不同的内容,我需要将适当的SelectedId值传递给UPDATE存储过程,并修改People表中该特定Person的值。

这就引出了一个终极问题:

如何最好地设计Person对象,使其同时包含HairColor、EyeColor、Ethnicity、StateOrProvince的ID和Name,以便在显示信息时使用名称,但ID用于初始化更新DDL控件。。。并最终处理更新

正如我反思的那样。。。我得出的结论是,我需要创建类来表示HairColor、EyeColor、Ethnicity、StateOrProvince属性。

然后是我的Person类,而不是像这样:

public class Person
{
    string FirstName { get; set; }
    string LastName { get; set; }
    int HairColorId { get; set; }
    string HairColorName { get; set; }
    int EyeColorId { get; set; }
    string EyeColorName { get; set; }
    int StateOrProvinceId { get; set; }
    string StateOrProvinceName { get; set; }
    string StateOrProvinceCode { get; set; }
}

会扩展成这样的东西:

public class HairColor
{
    int Id { get; set; }
    string Name { get; set; }
}
public class EyeColor
{
    int Id { get; set; }
    string Name { get; set; }
}
public class StateOrProvince
{
    int Id { get; set; }
    string Name { get; set; }
    string Code { get; set; }
}
public class Person
{
    HairColor HairColor { get; set; }
    EyeColor EyeColor { get; set; }
    StateOrProvince StateOrProvince { get; set; }
    public Person()
    {
        // how do I initialize a Person from a SQL data row?
    }
}

但是,如果我的Person类确实像上面那样。。。从SQL查询返回的给定数据行中,我究竟该如何最好地初始化它(无论是单独初始化还是在集合中初始化)?我似乎记得我不应该在构造函数中更新东西(即this.HairColor=new HairColor(dr["HairColorId"),dr["HairColorName"];)。。。所以我想知道如何打电话给

public static IEnumerable<Person> GetPeople()
{
    ...
}

在我的BLL中,可能会在将数据添加到集合之前将其数据填充给每个用户?

真的希望有人能在这里给我一个"哈哈"的时刻。。。

如何设计包含查找表中属性的类

我认为你有正确的方法,为那些支持实体创建类(尽管我会把StateOrProvince放在一个单独的Address实体中,也许所有这些特性都放在一一个单独PersonTraits实体中)。

有很多方法可以解决这个问题。如果没有ORM,请查看数据映射器(也称为依赖映射),它可以用于从数据库查询映射到Person实例。这是映射程序代码的概要:

var row = ... // query database
var person = new Person(row["FirstName"], row["LastName"]);
person.EyeColor = new EyeColor(row["EyeColorID"], row["EyeColorName"]);
...

(您也可以使用某种单独的对象生成器。)

任何时候更新一个人,都会使用相关实体的ID更新所有支持信息。

UPDATE:类似于ORM的EF4非常强大,可以帮助您完成许多重复任务(如我所描述的映射)。重要的是保持您的体系结构的灵活性和作为一个可交换层的持久性。请查看此处以获取一些指导。此外,我发现《领域驱动设计》一书对于理解这种分离以及如何为实体建模非常重要。

我会将您的查找表镜像为枚举。然后在一个值中同时获得id和name。如果名称中包含无法在标识符中使用的字符,则可以轻松创建一个属性来处理附加数据。

附加信息(例如,示例代码,修改以适应,我用VB编写,因此需要转换为C#):

Namespace Company.Data
  Public Enum EyeColor As Int16
    Unknown = 0
    Brown = 1
    Blue = 2
    Green = 3
  End Enum
  Public Enum HairColor As Int16
    Unknown = 0
    Brown = 1
    Blond = 2
    Red = 3
    Pink = 4
  End Enum
End Namespace

Public Class Person
  Public Property EyeColor As EyeColor = EyeColor.Unknown
  Public Property HairColor As HairColor = HairColor.Unknown
End Class

由于使用的是枚举,因此各个枚举值将映射到数据库查找表键。因此,您可以使用aPersonObject.HairColor.ToString()获取显示器,也可以使用aPersonObject.HairColor 获取ID

您可以非常喜欢并使用一些代码生成(mabey T4模板)根据数据库中的值自动创建枚举。

您研究过EF中的"导航属性"吗?它将允许您将ID保留在主类(即Person)中,并通过导航属性引用字符串属性。例如,您将拥有:

人员p=[从EF数据上下文中获取记录]

p.state_id将引用状态的数字id,而p.state.Name将是状态的字符串名称。EF负责加载引用的状态记录。如果您首先使用数据库并定义了外键(有一些工具会先将数据库转换为代码),它甚至可以自动为您创建它们。