如何规避错误的数据库架构?

时间:2009-10-05 13:18:28

标签: asp.net sql design-patterns database-design data-modeling

我们的团队被要求为现有的SQL Server后端编写Web接口,该后端源于Access。

其中一个要求/约束​​是我们必须限制对SQL后端的更改。我们可以创建视图和存储过程,但是我们已经被要求按原样保留表/列。

SQL后端不太理想。由于缺少外键,大多数关系都是隐含的。有些表缺少主键。表名和列名不一致,包括空格,斜杠和井号等字符。

除了找一份新工作或要求他们重新考虑这项要求外,任何人都可以提供任何良好的模式来解决这个问题吗?

注意:我们将使用SQL Server 2005和ASP.NET与.NET Framework 3.5。

11 个答案:

答案 0 :(得分:8)

观点和同义词可以帮到你,但修复底层结构显然需要更多的工作。

我当然会再次尝试说服利益相关方通过不解决他们正在产生技术债务的潜在问题,这将导致代码速度变慢,最终达到债务可能瘫痪的程度。虽然你可以解决它,但债务将在那里。

即使使用数据访问层,如果您尝试扩展,数据库中的基础问题(例如您提到的键/索引)也会导致问题。

答案 1 :(得分:6)

简单:确保您拥有强大的数据访问和业务逻辑层。您必须避免从ASPX代码隐藏直接编程到数据库的诱惑!

即使有一个强大的数据库模式,我现在也是一种从不在代码隐藏中使用SQL的做法 - 这种做法只有在学会了它有其缺点的困难之后才会发展。

以下是一些有助于此过程的提示:

首先,研究ObjectDataSource类。它将允许您构建一个强大的BLL,它仍然可以在不使用直接SQL的情况下提供GridView之类的控件。它们看起来像这样:

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
    OldValuesParameterFormatString="original_{0}" 
    SelectMethod="GetArticles"   <-- The name of the method in your BLL class 
    OnObjectCreating="OnObjectCreating"   <-- Needed to provide an instance whose constructor takes arguments (see below)
    TypeName="MotivationBusinessModel.ContentPagesLogic">  <-- The BLL Class
    <SelectParameters>
        <asp:SessionParameter DefaultValue="News" Name="category" <-- Pass parameters to the method
            SessionField="CurPageCategory" Type="String" />
    </SelectParameters>
</asp:ObjectDataSource>

如果构建BLL类的实例需要传递参数,则需要OnObjectCreating链接。在您的代码隐藏中,实现如下:

    public void OnObjectCreating(object sender, ObjectDataSourceEventArgs e)
    {
        e.ObjectInstance = new ContentPagesLogic(sessionObj);
    }

接下来,实现BLL需要更多的东西,我将为您节省谷歌搜索的麻烦。这是一个与上述调用相匹配的实现。

namespace MotivationBusinessModel   <-- My business model namespace
{
    public class ContentPagesItem  <-- The class that "stands in" for a table/query - a List of these is returned after I pull the corresponding records from the db
    {
        public int UID { get; set; }
        public string Title { get; set; }  <-- My DAL requires properties but they're a good idea anyway
        ....etc...
    }

    [DataObject]  <-- Needed to makes this class pop up when you are looking up a data source from a GridView, etc.
    public class ContentPagesLogic : BusinessLogic
    {
        public ContentPagesLogic(SessionClass inSessionObj) : base(inSessionObj)
        {
        }

        [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]  <-- Needed to make this *function* pop up as a data source
        public List<ContentPagesItem> GetArticles(string category)  <-- Note the use of a generic list - which is iEnumerable
        {
            using (BSDIQuery qry = new BSDIQuery())  <-- My DAL - just for perspective
            {
                return
                    qry.Command("Select UID, Title, Content From ContentLinks ")
                        .Where("Category", category)
                        .OrderBy("Title")
                        .ReturnList<ContentPagesItem>();
                 // ^-- This is a simple table query but it could be any type of join, View or sproc call. 
            }
        }
     }
 }

其次,很容易将DAL / BLL dll作为附加项目添加到项目中,然后添加对主Web项目的引用。这样做不仅可以为您的DAL和BLL提供自己的身份,而且还可以轻松进行单元测试。

第三,我几乎不愿意承认它,但这可能是微软实体框架派上用场的地方。我通常不喜欢Linq to Entities,但它确实允许您在数据库中缺少的数据关系的代码端规范。

最后,我可以看到为什么更改到你的数据库结构(例如移动字段)会有问题但是添加新约束(尤其是索引)不应该。他们是否担心外键最终会导致其他软件出错?如果是这样......这不是一件好事;你必须有一些痛苦才能知道疾病所处的位置,不是吗?

至少,出于性能原因,您应该根据需要推动添加索引的能力。此外,我同意其他人认为,观点可以大大有助于使结构更加明智。 但是,从长远来看,这还不够。所以...继续构建视图(存储过程也是如此),但是你应该仍然避免直接编码到数据库。否则,您仍然将您的实现锚定到数据库模式,并且在将来转发它时将比将数据库交互隔离到DAL更难。

答案 2 :(得分:4)

我以前来过这里。管理层认为使用旧架构会更快并向前发展。我试图说服他们,新的架构会导致这种情况以及所有未来的发展变得更快,更强大。

如果您仍然被击落并且无法完成重新设计,请妥协处理它们,您可以在其中重命名列和表,添加PK,FK和索引。这不应该花费太多时间并且会有很多帮助。

如果你不得不磨掉它。我将所有内容封装在存储过程中,您可以在其中添加各种检查以强制执行数据完整性。我仍然会尽可能地隐藏所有PK,FK,索引和列重命名。

答案 3 :(得分:2)

由于您声明需要“限制”对数据库架构的更改,因此您似乎可以将主键字段添加到那些没有它们的表中。假设现有应用程序没有执行任何“SELECT * ...”语句,这不应该破坏现有代码。完成之后,创建统一方法的表的视图,并在视图上为INSERT,UPDATE和DELETE语句设置触发器。它会有轻微的性能损失,但它将允许数据库的统一接口。然后添加的任何新表都应符合新标准。

非常hacky处理它的方式,但这是我过去采用的一种方法,只有有限的访问权限来修改现有的模式。

答案 4 :(得分:2)

如果遇到这个问题,我会做一个“结束”。告诉你的老板,处理这个问题的最佳方法是创建一个“报告”数据库,它实际上是原始数据库的动态副本。您可以创建脚本或单独的数据泵应用程序来更新报告数据库,更改原始数据库,并将对数据库所做的更改传播回原始数据库,然后编写Web界面以仅与“报告”数据库进行通信。

一旦你有了这个设置,你就可以自由地合理化你的数据库副本,并通过添加主键,索引,规范化等将其恢复到理智的境界。即使在短期内,这是一个比尝试编写Web界面与设计不良的数据库进行通信更好的选择,并且从长远来看,您的数据库版本可能最终会替换原始数据库。

答案 5 :(得分:1)

  

任何人都可以为解决这一不足提供任何良好的模式

似乎你被剥夺了“解决这个缺陷”的唯一可能性。

我建议将完整性检查的逻辑添加到即将到来的存储过程中。外键,唯一键,都可以手动强制执行。不是一件好事而是可行的。

答案 6 :(得分:1)

你可以做的一件事是创建大量的视图,这样表和列名称可以更合理,摆脱麻烦的角色。我倾向于不喜欢在我的表/列名称中使用空格,这样我就不必在任何地方都使用方括号。

不幸的是,由于缺少外键,您将遇到问题,但您可能希望在列上创建索引,例如名称,这些索引必须是唯一的,以提供帮助。

至少在光明的一面,你不应该有很多连接要做,所以你的SQL应该很简单。

答案 7 :(得分:0)

如果使用LinqToSql,则可以在DBML中手动创建关系。此外,您可以将对象重命名为首选标准(而不是斜杠等)。

答案 8 :(得分:0)

我将从定义主键的每个表的索引视图开始。也许在您希望表上的索引的地方有更多的索引视图。这将开始解决您的一些性能问题和所有命名问题。

我会将这些视图视为原始表格。这是您的存储过程或数据访问层应该操作的内容,而不是基表。

在强制执行完整性规则方面,我更喜欢将其放在代码中的数据访问层中,只在数据库层进行最少的完整性检查。如果您更喜欢数据库中的完整性检查,那么您应该广泛使用sprocs来强制执行它们。

不要低估找到新工作的可能性; - )

答案 9 :(得分:0)

这取决于您拥有多少自动化测试覆盖率。如果你很少或没有,你不能在没有破坏的情况下进行任何改变(如果它还没有被打破)。

我建议您在进行其他更改时合理地重构内容,但不要对数据库进行大规模重构,它会引入错误和/或创建过多的测试工作。

缺少主键这样的事情确实难以解决,应该尽快纠正。基本上可以永远容忍不一致的表名。

答案 10 :(得分:0)

我使用设计不佳的数据库进行了大量数据集成,而我采用的方法是使用与原始源数据库并列的交叉引用数据库;这允许我将视图,存储过程和维护代码存储在关联的数据库中,而无需触及原始模式。我在触摸原始模式时遇到的一个例外是,如果需要,我将实现索引;但是,如果可能的话,我会尽量避免这种情况。

这种方法的优点是可以将旧代码与旧代码隔离开来,并且可以创建视图来解决不良实体定义,命名错误等问题,而不会破坏其他代码。

斯图