ASP.NET网站架构问题

4
由于我所工作的网站的设计方式的特性,我现在必须解决一个问题。 我会尝试以无语言依赖的方式写出这个问题,但是该网站是用ASP.Net和C#完成的。
我们网站的结构如下所示: 对于任何给定的“对象”,它的结构如下:
ASPX页面 多个用户控件(ascx页面) 文本框、组合框、标签、按钮等
这意味着,如果我们有一个用户页面,那么该页面可能有3个用户控件。一个用于用户信息(姓名,电子邮件等),一个用于地址(城市,州,邮政编码等),一个用于区域设置(时区,语言等)。
这3个用户控件中的所有字段都存储在数据库的同一张表中。
现在,我们正在使用DataBinding和EntityFrameworks来填充数据,这意味着当我们保存一个对象时,我们实际上是调用save 3次(每个UserControl一次)。
因为UserControl只知道它包含的字段,所以当Databinding保存时,“它认为”其他字段现在为空(即Address控件保存后,“Email”和“Name”现在是空值)。为了“修复”这个问题,有一种方法叫做“MapOldToNew”,它会去获取旧记录并在即将保存的新对象上填充值。这是通用的,这意味着我们没有一个在Address Usercontrol上的方法,说“去获取Name和Email字段并填充它们”,而是遍历实体(对象)的所有属性,如果新对象上的值为NULL但旧对象(即将被覆盖的当前数据库中的那个对象)上的值不为NULL,它将使新值等于旧值。
问题是日期。我们允许在数据库中使用NULL日期。
如果用户填写了“生日”并保存,他们现在在数据库中的该字段中有一个生日值。 如果他们回到网页并清除生日字段并保存,MapOldToNew方法会获取旧记录,看到生日在旧记录中不为null,在即将保存的新对象上为null,它将用旧值(例如'7/23/1981')覆盖新值(NULL)。
解决方案?
  1. 重新设计我们的系统,不使用UserControls,而是将所有控件放在同一页上,从而减少保存次数并且不需要MapOldToNew。现在,让我们假设这是一个不可能的解决方案(即使出于很多原因我觉得应该这样做)。
  2. 将所有日期存储为字符串,并在加载和保存时进行转换。(字符串没有同样的问题,因为一旦字符串被修改,它现在是一个空字符串,而不是NULL)
  3. 不允许数据库中有NULL日期,始终存储DateTime.MinValue并且不要在加载时显示它。

这些是我头脑中想到的想法。为了论证,让我们假设#1不可能(因为我感觉管理层不会喜欢它需要花费的时间)。

你会如何“修复”这个问题?

另一个解释

这里是更详细地分解问题。用户记录有两个字段。名称和出生日期。
假设有一个带有两个UserControls(ascx)的ASPX页面。
UserControl1有一个单独的DatePicker DataBound到“BirthDate”。
UserControl2有一个单独的TextBox DataBound到“Name”。
当UserControl1对ObjectContext调用Update时,它发送的User对象如下:

{ Name = NULL, BirthDate = 8/13/1980 }  

MapOldToNew查看数据库记录并发现Name应该是"Bob",因此填充该值并保存记录。

UserControl2现在调用更新,用户对象如下:

{ Name = "John", BirthDate = NULL }

MapOldToNew发现BirthDate为NULL,因此它会从数据库中覆盖它并保存记录,数据库中的值是正确的。

现在假设用户回去并删除了BirthDate的值。当UserControl2调用MapOldToNew时,它会看到BirthDate为NULL(用户想要这样),并从数据库中覆盖它。
如果它没有从数据库中覆盖它,它将始终将其保存为空,并且用户将无法设置BirthDate。


当然,这个问题与编程语言无关。否则,如果您在使用VB.NET进行编程,则可能不会遇到完全相同的问题。 - John Saunders
是的,但它也有点儿 ASP.Net 不可知。从 MVC 模式的角度来看它。一个 View 有多个 SubView,它们都调用相同的 Controller.Save 方法,使用部分填充的 Model,因此它们都调用 MapOldToNew 方法,将 NULL 日期从数据库中的值进行覆盖。 - James P. Wright
你的问题在于 MapOldToNew 函数。我不熟悉 Entity-Framework,但应该可以从数据库中加载旧值,覆盖 UC 中的列并保存它。如果能够部分更新记录(仅更改的列),那就更好了,这是可能的吗? - Tim Schmelter
1
在谷歌上搜索了几分钟后,我找到了存根实体。看一下这个视频:http://www.screencast.com/t/ZGQyY2I5ZWE - Tim Schmelter
@Tim - 这可能是需要关注的事情,但它需要手动填充100个以上的UserControls上的50个以上实体。 - James P. Wright
1个回答

1
我会重构MapOldToNew以理解可空类型。我假设你有一个可空日期(例如datetime?或Nullable),如果是这样,请不要做出覆盖null的假设。
此外,在将来考虑MVC或对于WebForms,请考虑Dynamic Data,因为它可以将类型映射到模型中。 http://aspnet.codeplex.com/wikipage?title=Dynamic%20Data

是的,但在这种情况下,您将如何重构MapOldToNew?如果更新实体上的日期为NULL,我们如何知道它是否为NULL是因为用户将其清空,还是因为它只是未在调用更新的UserControl中? - James P. Wright
2
如果您总是进行这三个更新操作,那么您肯定不想全部保存三次。您需要将其合并为一个单一模型,以在保存之前映射每个用户控件的值,或者创建一个新的组合控件来处理此问题。如果您不想重构以进行一次保存,那么您只需要更新该信息的一部分-而不关心其他内容。例如,您的更新过程只需要知道地址是否为空,如果地址为空,则更新为null;如果地址不为空,则分配一个值-并且不要触及任何其他字段。虽然有三个更新操作,但我肯定会考虑重构。 - Adam Tuliper
我完全同意你对重构的看法。问题在于我的例子并不是真实情况,只是一个简化的解释。实际上可能有50多个实体和数百个用户控件需要重构。我正在寻找一种无需重构就能完成的方法。我相信这是不可能的,但我希望比我聪明的人能想出一种方法。 - James P. Wright
但是,如果你们都走了这种常规方法,那么只需重构该方法即可。如果这个OldToNew方法假定它将保持原始值 - 那么你需要在该方法中改变那个行为 - 没有其他方式,除非重构其他所有内容。你有一个优势,就是通过一个通用的方法 - 不看整个系统,只看一个简化的情况,这是我看来的方式。那么,你能改变MapOldToNew以改变这种空值行为吗?你能举个例子说明为什么会失败吗 - 比如实体行为? - Adam Tuliper
在上面发布一个不同的解释。 - James P. Wright
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接