65.9K
CodeProject 正在变化。 阅读更多。
Home

D.O.T.S. - 分布式混淆状态传输

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (9投票s)

2013年5月2日

CPOL

20分钟阅读

viewsIcon

19157

一项关于使用Azure云服务通过多项技术进行通信的研究,所有技术都基于一个集中的信息域

目录

  1. 网站和源代码
  2. 引言
  3. 挑战1 - 概述 - 计划
  4. 挑战2 - D.O.T.S. 网站
    1. 彩蛋
    2. 关键原则和构建网站
    3. HTML 5 设计
    4. CSS和响应式设计
    5. JavaScript - DOTS地图

网站和源代码

引言

沟通 - 信息交换:人与人之间信息的交换,例如通过说话、写作或使用共同的符号或行为系统。

D.O.T.S. 是一系列简单的彩色和纹理圆圈(点)的系统化关系,它们被用作一种共同的符号系统,帮助两个人或更多人匿名地进行通信。

DOTS 本质上是一个非常简单的想法。该项目的核心将是一个3x3的圆圈网格,这些圆圈可以是透明的,也可以包含最多三种彩色和纹理的图案。只要所有参与者都知道预定的图案,这个网格就可以传输编码消息给两个或更多参与者。

挑战1 - 概述 - 计划

本文是为了提交2013年CodeProject Azure挑战赛而撰写的。

DOTS,尽管想法简单,但面临着更大的挑战。最终项目将有许多不同的接口。我希望能够实现网站、移动应用程序、各种API,使用HTML、JavaScript、Web API和WCF等多种技术,所有这些都托管在Azure上并由Azure支持。计划是,虽然DOTS将有一个基础网站,但它可以与几乎任何能够连接到云的技术进行交互或只读访问。

DOTS作为通信协议可以有无限的用途。网站可以嵌入DOTS地图来显示系统状态;公司员工可以使用DOTS来传达他们不希望写下来的消息;恋人可以通过符号互诉衷肠。想法无穷无尽。我希望展示DOTS协议如何被定义,以便在预定的DOTS地图之上构建更大型的应用程序。

第一个挑战:入门

本文

第二个挑战:构建网站

这个网站将不连接数据库,所以我希望在此挑战中实现JavaScript、移动优先响应式设计(CSS)以及可能开始使用Web API。虽然网站将基于HTML5,但我计划使用JavaScript来确保旧浏览器也能访问该网站。

第三个挑战:在Azure上使用SQL

在此挑战中,我将连接DOTS的社交网络部分。用户将能够匿名注册并创建新的群组来共享他们的DOTS地图。其他用户将能够轻松地加入DOTS群组,而无需注册。群组中的任何人都将能够访问DOTS地图。DOTS的设计将不包含访问权限,因为其等同的访问设计。

第四个挑战:虚拟机

在此挑战中,我将设置我的Windows Azure服务,这些服务将清理DOTS群组以及我将用作移动挑战API和通用访问点的WCF服务。

第五个挑战:移动访问

移动应用程序将以Windows 8手机为目标(因为我拥有它),但将使用JavaScript、Web API或WCF调用与Azure进行交互。我的目标不是将其绑定到任何一个操作系统,所以我将尝试保持其通用HTML5和JavaScript。这将使其易于迁移到许多移动环境。

我对移动应用程序不太熟悉,所以这将是我要应对的最具挑战性的挑战。

挑战2 - D.O.T.S. 网站

复活节彩蛋

我的复活节彩蛋是Bob在旋转Dots并说话。要看到他,请用蓝色填充Dots制作一个绿色的字母C,如下图所示。弹出的复活节彩蛋不是动画PNG。它是由jQuery Rotate插件和所有JavaScript实现的。我时间不够,没能在文章中详细说明我是怎么做的,但如果你有兴趣,可以查看上面的源代码。

关键原则和构建风格

源代码

我特意将第一个挑战设计成一个非常基础的网站。我没有将其纳入更大的框架,这样我就可以展示JavaScript和CSS本身的力量。从现在开始,我的源代码将包含在一个更大的框架内(可能是MVC 4)。

领域驱动设计

领域驱动设计本身并不是一种方法论、框架或设计模式。它更像是一种思维方式。它将焦点放在领域逻辑上,所有工作都围绕该逻辑进行。这只是确保所有设计都聚焦于一个中心目标的方式。

DOTS项目的主要设计原则将是基于技术的协议的可扩展模型。通过先完成前端网站,我需要确保能够将其包装到更大的框架中,并连接到数据库和其他技术。因此,我重点关注了CSS、JavaScript以及DOTS映射的基本工作方式。

框架/可扩展性

只要有可能,我都会尝试使用“购买而非自建”的设计方法。我通常只使用知名的第三方框架。在本文中,我将仅使用开源框架,除了Microsoft Azure、.NET和Visual Studio产品。

HTML5 设计

HTML5为我们提供了许多新的Web开发工具。其中许多新扩展是为了响应网站世界中出现的新动态领域。过去,我们设计网站只供在计算机和计算机显示器上查看。在当今世界,我们需要网站通过多种媒体查看器(如手机、平板电脑、混合设备以及桌面)可用。网站还需要在客户端拥有更强大的存在感,让客户端(浏览器)承担比以往更多的工作。

文章假设

由于竞赛的时间限制,我无法深入探讨每个术语及其含义,因此本文将面向中级或高级Web开发人员。我将假设读者具备HTML、CSS、JavaScript,可能还有jQuery的基础知识。我将尝试展示示例和使用的框架。大部分内容将是概述,如果有时间,我会深入探讨一些较酷的功能代码。如果您希望我详细解释我为什么或如何做某事,请随时与我联系。

CSS和响应式设计

响应式设计是指确保网页在任何类型的媒体上都具有清晰的显示效果,而无需为每种设备编写新的网站。围绕响应式设计的CSS样式被称为媒体查询。在我们深入探讨这些之前,让我们先讨论一个非常流行的框架,它不仅限于响应式设计;Twitter Bootstrap。

Twitter Bootstrap

Twitter Bootstrap是一套出色的界面元素、CSS布局和JavaScript工具,打包在一起,可免费用于网站设计。如果您正在设计新网站,我强烈建议您了解这个框架。我相信您会从中找到有用的东西。

Twitter Bootstrap的脚手架是该框架的一部分,它帮助开发人员设置响应式设计,并且是我在网站中使用的第一个关键技术。脚手架使用一个12列的网格系统,它会尝试将您的网页部分组合在一起,由特定的Bootstrap类定义。随着网页宽度的减小,右侧的组合将向下移动到左侧的组合下方,直到所有组合都排列成一行。这样,在小型设备上,您可以自上而下阅读网页,而在显示器上,您可以从左到右,然后从上到下阅读元素组。

Bootstrap脚手架的响应式功能还提供预定义的CSS/JavaScript类,这些类会根据其是否针对特定类型的媒体来显示或隐藏代码段。这对于轻松定义显示和隐藏基于您正在设计的设备非常有用。我最初的网站上并未过多使用这些类,只是因为我想展示使用媒体查询的不同想法。

Twitter Bootstrap

退化式设计 vs. 移动优先 CSS

移动优先和退化式设计都是使用CSS3媒体标签来根据页面宽度改变设计外观和感觉的想法。退化式设计是指首先为大型媒体(如桌面)编码默认布局,然后使用媒体查询来调整较小宽度的布局。另一方面,移动优先设计通常在每个媒体查询中随着浏览器的增大而进一步改进设计。将移动优先设计视为CSS的渐进增强,而退化式设计则是从完整的CSS开始,并优雅地移除较小宽度下不需要的样式。这两种设计模式都很有用,应根据项目需求进行考虑。

我选择退化式设计而不是移动优先的主要原因是,旧版浏览器(如IE 6-8)不识别媒体查询,因此用户将看到完整网站而不是移动版本。有一些JavaScript库可以使这些旧版浏览器能够使用媒体查询,但我只是选择不使用它们,除非有需要。

下面展示了媒体查询在CSS文件中的工作方式。默认网站CSS放在顶部,然后是Table、Wide Mobile和Non-smart phones的每个更改,依次放在下面。

/*        Tablet Layout: 768px.
-----------------------------------------------------------------
*/

@media only screen and (min-width: 768px) and (max-width: 991px) {
}

/*        Wide Mobile Layout: 480px.
------------------------------------------------------------
*/

@media only screen and (min-width: 480px) and (max-width: 767px) {
}

/*        Misc Small Mobile Mobile Layout: < 480px.
------------------------------------------------------------
*/

@media only screen and (min-width: 1px) and (max-width: 479px) {
}

JavaScript - 当CSS不够用时

响应式设计主要处理网站的宽度,因为向下滚动通常不是问题。然而,在我的DOTS网站上,我特意添加了一个页脚,以便展示如何删除一个元素,如果它与其他元素发生冲突。这是需要知道如何做但不能总是用CSS完成的一个领域。

基本的HTML页脚

<div class="footer">
    <div>
        &#169; DOTS 2013. All Rights Reserved
    </div>
</div>

页脚CSS。请注意在此CSS代码中,绝对定位,底部为0,宽度为屏幕的100%,这使得页脚始终保持在底部。

/*------------------------- Footer -------------------------*/
.footer {
   position:100%;
   height:50px;   /* Height of the footer */
   margin:0 10px;
   padding:0;
}

用于在页脚超出.dotsTable类时隐藏页脚的JavaScript。

$(function () {
        /* Hide Footer if window goes over the Dots Table */
       $(window).resize(function() {
           $('.footer').show();

           var dotsTable = $(".dotsTable");
           var tablebottom = dotsTable.position().top + dotsTable.height();
           var footertop = $(".footer").position().top;

           if (footertop <= tablebottom)
               $('.footer').hide();
       });
});

在上面的jQuery代码中,请注意我首先确保页脚是显示的。如果一个对象被隐藏,您将无法获取其位置。jQuery有一个position函数,可以返回元素在DOM窗口中的顶部和左侧像素位置。通过将height函数添加到此值,您可以获得底部,我在这里对dotsTable进行了操作。

然后我测试页脚的顶部是否在表格底部之上,如果是,我就会隐藏它。这是相当直接的代码,但在测试和修复窗口大小调整时元素交叉的问题时很重要。

JavaScript - DOTS地图

DOTS地图是一个简单的HTML表格。我决定不使用<img>标签作为按钮,而是将按钮作为背景图像。这要好得多,因为表格会响应式地缩放,让图像随着它一起增长和收缩,但这有一个诀窍。让我用HTML和CSS来展示这一点,从而实现这种功能。

HTML非常简单,这也是HTML5页面设计的目标。请注意,它只是一个空的表格,没有任何内容。

<div class='dotsMap'>
  <div id="dotsSelect">
    <table class="dotsTable">
       <tr>
         <td id="r1c1"></td>
         <td id="r1c2"></td>
         <td id="r1c3"></td>
       </tr>
       <tr>
         <td id="r2c1"></td>
         <td id="r2c2"></td>
         <td id="r2c3"></td>
       </tr>
       <tr>
         <td id="r3c1"></td>
         <td id="r3c2"></td>
         <td id="r3c3"></td>
       </tr>
    </table>
  </div>
  <div id="colorSelect">
      Select color
      <div class="colors" id="red"></div>
      <div class="colors" id="green"></div>
      <div class="colors" id="blue"></div>
  </div>
</div> 

这是负责默认背景点的CSS。请注意,在这里我们将每个<td>的背景图像设置为'contain'的大小。这允许图像随着单元格的大小而增长或收缩。这是一种使表格真正响应式的好方法。或者,如果我们在单元格内放置<img>标签,我们就需要使用JavaScript或根据特定宽度替换新图像。

.dotsTable tr td  {
    background:url(../images/dots/silverDot.png);
    background-size:contain<;
   width<: 124px;
   height: 124px;
}</</

从这里开始,jQuery接管并提供了我们更改点颜色的全部功能。触发DOTS设置的三个事件是mousedownmouseentermouseup。这些都是鼠标事件,所以目前只有桌面上的鼠标或触摸屏上的手指才能设置点。我正在考虑未来如何让键盘选择点,但目前还不行。

/* dot definitions */
var _imgFolder = "./images/dots/";
var _silverDot = "silverDot.png";
var _redDot = "redDot.png";
var _greenDot = "greenDot.png";
var _blueDot = "blueDot.png";

/* defaulting variables */
var _mouseDown = 0;
var _currentColor = "rgb(255, 0, 0)";

 /* Mouse Down - Start our new object */
 $(function () {
    $('.dotsTable td')
      .mousedown(function () {
        _mouseDown = 0;

            var silverPath = (_imgFolder + _silverDot);
            var img = _redDot;
            if (_currentColor == 'rgb(255, 0, 0)')
                img = _redDot;
                else if (_currentColor == 'rgb(0, 128, 0)')
                    img = _greenDot;
                    else if (_currentColor == 'rgb(0, 0, 255)')
                        img = _blueDot;

            var bgImage = $(this).css('background-image');
            if (bgImage.indexOf(img) < 0)
            {
                $(this).css('background-image', 'url(' + _imgFolder + img + ')');

                if (img == _redDot)
                {
                    $(this).data('toggle', "1");
                    $(this).data('color', "1");
                }                    
                else if (img == _greenDot)
                {
                    $(this).data('toggle', "1");
                    $(this).data('color', "2");
                }                    
                else if (img == _blueDot)
                {
                    $(this).data('toggle', "1");
                    $(this).data('color', "3");
                }                    
                else 
                {
                    $(this).data('toggle', "0");
                    $(this).data('color', "0");
                }                    
            }
            else
            {
                $(this).css('background-image', 'url(' + silverPath + ')');
                $(this).data('toggle', "0");
                $(this).data('color', "0");
            }

            _mouseDown = 1;
              })

        .mouseenter(function () {
                if (_mouseDown == 1)
                {
                    var silverPath = (_imgFolder + _silverDot);
                    var img = _redDot;
                    if (_currentColor == 'rgb(255, 0, 0)')
                        img = _redDot;
                        else if (_currentColor == 'rgb(0, 128, 0)')
                            img = _greenDot;
                            else if (_currentColor == 'rgb(0, 0, 255)')
                                img = _blueDot;
                    
                    var bgImage = $(this).css('background-image');
                    if (bgImage.indexOf(img) < 0)
                    {
                        $(this).css('background-image', 'url(' + _imgFolder + img + ')');

                        if (img == _redDot)
                        {
                            $(this).data('toggle', "1");
                            $(this).data('color', "1");
                        }                    
                        else if (img == _greenDot)
                        {
                            $(this).data('toggle', "1");
                            $(this).data('color', "2");
                        }                    
                        else if (img == _blueDot)
                        {
                            $(this).data('toggle', "1");
                            $(this).data('color', "3");
                        }                    
                        else 
                        {
                            $(this).data('toggle', "0");
                            $(this).data('color', "0");
                        }                    
                    }
                    else
                    {
                        $(this).css('background-image', 'url(' + silverPath + ')');
                        $(this).data('toggle', "0");
                        $(this).data('color', "0");
                    }
                }
        })

        .mouseup(function () {
                _mouseDown = 0;
                saveDots();
   });

    /* Choose Color for Dots */
    $('.colors').click(function() {
        $(this).fadeTo("fast", 0.40)
        .css('border', '2px solid black');

        _currentColor = $(this).css("background-color");
        $('.colors').not(this).fadeTo("fast", 1)
        .css('border', '0');
    });

    /* Default Data and colors */
    $(document).ready(function() {
        $('.colors#red')
        .fadeTo("fast", 0.40)
        .css('border', '2px solid black');

        $('#r1c1').data('toggle', "0");
        $('#r1c2').data('toggle', "0");
        $('#r1c3').data('toggle', "0");
        $('#r2c1').data('toggle', "0");
        $('#r2c2').data('toggle', "0");
        $('#r2c3').data('toggle', "0");
        $('#r3c1').data('toggle', "0");
        $('#r3c2').data('toggle', "0");
        $('#r3c3').data('toggle', "0");

        $('#r1c1').data('color', "0");
        $('#r1c2').data('color', "0");
        $('#r1c3').data('color', "0");
        $('#r2c1').data('color', "0");
        $('#r2c2').data('color', "0");
        $('#r2c3').data('color', "0");
        $('#r3c1').data('color', "0");
        $('#r3c2').data('color', "0");
        $('#r3c3').data('color', "0");
    });

});

/* Get the Dots Current Status and save through Ajax */
function saveDots() {
    var dot1 = $('#r1c1').data('toggle') + '.' + $('#r1c1').data('color');
    var dot2 = $('#r1c2').data('toggle') + '.' + $('#r1c2').data('color');
    var dot3 = $('#r1c3').data('toggle') + '.' + $('#r1c3').data('color');

    var dot4 = $('#r2c1').data('toggle') + '.' + $('#r2c1').data('color');
    var dot5 = $('#r2c2').data('toggle') + '.' + $('#r2c2').data('color');
    var dot6 = $('#r2c3').data('toggle') + '.' + $('#r2c3').data('color');

    var dot7 = $('#r3c1').data('toggle') + '.' + $('#r3c1').data('color');
    var dot8 = $('#r3c2').data('toggle') + '.' + $('#r3c2').data('color');
    var dot9 = $('#r3c3').data('toggle') + '.' + $('#r3c3').data('color');

    /*alert ('saveDots ' + dot1 + ' ' + dot2 + ' ' + dot3 + ' ' + dot4 + ' ' + 
        dot5 + ' ' + dot6 + ' ' + dot7 + ' ' + dot8 + ' ' + dot9)*/
    /* Show Bob Easter Egg */
    if ((dot1 == "1.2") && (dot2 == "1.2") && (dot3 == "1.2")
        && (dot4 == "1.2") && (dot5 == "1.3") && (dot6 == "1.3")
        && (dot7 == "1.2") && (dot8 == "1.2") && (dot9 == "1.2")) 
        sayHiBob();

    /* Send Ajax from Here */

}

Mousedown事件开始选择DOTS。此时,用户可以通过Mouseenter事件将鼠标拖动到其他按钮上,或释放鼠标触发Mouseup事件。每当发生Mouseup事件时,我们都会触发SaveDots方法,该方法最终会发出AJAX调用到服务器以保存DOTS的当前状态。

请注意,在JavaScript中,我使用jQuery的.data命令来保存DOTS信息。这是一个保存信息的不错方法,但也有一些需要注意的地方。数据存储在jQuery缓存中,所以您必须使用.data命令检索数据,而不是.attr命令。人们倾向于犯这个错误的原因是,jQuery中的.data函数会尝试根据数据将存储的变量转换为相应的数据类型。在示例中,"1.4"将返回为数字而不是字符串,而使用.attr函数存储数据将始终使其成为string

发布到Azure

本文重点介绍在Azure中构建网站,因此需要有关如何操作的说明。随附有一个很棒的视频,其中展示了创建网站并将其发布到Azure的简单过程。Azure确实让快速启动网站变得很简单。

<OBJECT type="application/x-shockwave-flash" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=3,0,0,0" WIDTH="640" HEIGHT="300" data="http://www.youtube.com/v/TZgFPdcKSkM?hl=en_US&version=3"><param name="movie" value="http://www.youtube.com/v/TZgFPdcKSkM?hl=en_US&version=3" /><param name="quality" value="high" /><param name="wmode" value="transparent" /></OBJECT>

第三个挑战:在Azure上使用SQL

深入探讨! - 定义领域

系好安全带,抓紧了,因为第三个挑战是我们将真正开始在DOTS文章中前进的地方。之前,我曾谈论过领域驱动设计。现在可以通过从数据库开始,通过项目的不同层来展示项目的真正架构和强大之处。通过演示每一层,希望能够表明,虽然DOTS表面上是一个简单直接的想法,但其真正的意图是有一个良好的布局,可以从中扩展和共享几乎任何大小的网站。

在上面的图表中,我们展示了一个数据层,它隐藏了数据库,但可供我们所有的接口和API使用。这允许我们集中模式定义,以便所有人都始终保持同步并可供我们的通信层使用。通过这种架构,通信层的每个部分都很容易在发生CRUD服务时共享信息。也更容易为项目添加新的可伸缩层,例如缓存层。

[关键点] 通过这种方式设计,DOTS部分实际上只是所有能够与我们的通信层通信的设备之间共享的一组信息。它的美妙之处在于,项目看起来集中的主题(DOTS)只不过是一个插件。

杂货清单、待办事项清单、集中式日志,甚至是社交网络群组都可以采用这种模式而不是Dots。而且更好的是,信息现在可以在所有能够访问API级别的设备之间共享;智能手机、网站、平板电脑、服务器,甚至是Winform应用程序。

这才是Azure如此酷的原因!为了方便起见,一个托管服务包罗万象。过去,一个人需要一个拥有或租赁的服务器网络来完成这种类型的站点,然后可伸缩性将一直困扰软件。现在,我们可以拥有这一切,并且还能“分享蛋糕”

Azure – 创建数据库

我们将从数据优先设计开始。我喜欢先在本地SQL Server或Express中创建我的数据库,这样我就可以使用SQL Server的设计模式。

我们需要假设程序员已经知道如何创建数据库,所以让我们从使用SQL Server Express 2012中设计好的本地数据库开始,然后展示如何将其导入Azure。让我们通过右键单击数据库并从“任务”下拉菜单中选择“生成脚本”来开始。

单击“下一步”,直到向导询问脚本应如何保存或发布。选择“剪贴板”,但不要单击“下一步”。先单击“高级”按钮。

在“高级脚本选项”中,请确保您将“脚本用于数据库引擎类型”下拉菜单并选择“SQL Azure数据库”,如图所示。

然后,单击OK并按照其余提示进行操作。

现在您已将脚本保存在内存中。是时候登录SQL Azure数据库了。SQL Azure服务器应已在本篇文章的开头部分设置好。请通过Management Studtio登录此服务器。

打开一个新的查询窗口。粘贴并运行脚本。就这样,您现在将拥有本地SQL到SQL Azure的数据库的镜像副本。

数据层

存储库和单元工作模式

我们决定使用存储库和单元工作模式来构建我们的数据访问层(DAL)。通过使用这些模式,所有通过任何框架(例如WebAPI、WCF、MVC)的通信都将拥有一个中心访问点。这提供了数据与所有CRUD服务到数据库之间清晰且持久的交互。当数据库发生更改时,我们只需在一个地方更改数据库模式图,而不是在所有项目中更改。

Linq to Entities将作为我们DAL中的对象关系映射(ORM),主要因为Microsoft与Azure和Entities配合得非常好。本文没有理由混淆这种关系,但请注意,如果存在其他喜欢的ORM,实现这种模式并不难。

这是一个很好的链接,可以更好地解释和教授存储库和单元工作模式

开始项目

让我们首先在解决方案中创建一个新项目,并将其命名为DOTS.Data。您可以看到我命名约定。

  • DOTS.Web - Web项目
  • DOTS.Data – 数据访问层项目
  • 稍后,我将介绍DOTS.WebAPIDOTS.WCF等...

Add New DAL Project

构建我们的存储库模式

现在,让我们构建我们的.NET实体数据模型。右键单击我们的项目 -> 添加新项 -> ADO.NET实体数据模型 -> 从数据库生成 -> 将实体连接设置为Azure连接字符串

New Entity Data Model

选择Azure连接字符串或创建一个到Azure数据库的新连接。我们有之前创建的DotsConnectionString

DataConnection

在我们的数据优先模型中,我们将从现有数据库构建模型。我们也可以反过来做,但我通常先创建我的数据库。如果您知道您正在编程的平台(如本例中的Azure),那么我喜欢这样做数据优先建模,因为我可以设计并避免数据库引擎的任何限制,而不是尝试将模型转换为数据库。在这个项目中,这更多是一种偏好,而不是正确或错误的做法。

Select Database

选择所有表,以便可以从它们全部创建映射。

Choose Tables

现在应该显示一个模型图,如下所示。

Model Diagram

现在是时候创建我们新存储库的接口了。添加新项 -> 接口 -> 命名为IRepository.cs

Make Interface

接口将保证我们的存储库将遵循的规则。将其视为与存储库交互的任何人的契约,以确保我们创建的东西是正确的。这是我们为此编写的代码。

using System.Linq;

namespace DOTS.Data
{
    public interface IRepository<t> where T : class
    {
        IQueryable GetAll();
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Delete(T entity);
        void Delete(int id);
    }
} 

好了,让我们创建实现上面接口的实际存储库。添加新项 -> -> Repository.cs

Repository

这是我到目前为止的存储库样子。

using System;
using System.Data;
using System.Data.Entity;
using System.Linq;

namespace DOTS.Data
{
    public class Repository<t> : IRepository<t> where T : class
    {
        protected DotsEntities DbContext { get; set; }
        protected DbSet<t> DbSet { get; set; }

        public Repository(DotsEntities dbContext)
        {
            if (dbContext == null) throw new NullReferenceException("dbContext");
            DbContext = dbContext;
            DbSet = dbContext.Set<t>();
        }

        #region Implementation of IRepository<t>

        public IQueryable GetAll()
        {
            return DbSet;
        }

        //public T GetById(int id)
        //{
        //    return DbSet.Find(id);
        //}

        public T GetById(int id) 
        {
            var someEntity = DbSet.Find(id);
            return someEntity;
        }

        public void Add(T entity)
        {
            DbSet.Add(entity);
        }

        public void Update(T entity)
        {
            var entry = DbContext.Entry(entity);
            DbSet.Attach(entity);
            entry.State = EntityState.Modified;
        }

        public void Delete(T entity)
        {
            var entry = DbContext.Entry(entity);
            DbSet.Attach(entity);
            entry.State = EntityState.Deleted;
        }

        public void Delete(int id)
        {
            var entity = GetById(id);
            if (entity == null) return;
            Delete(entity);
        }

        #endregion
    }
}

好了;现在我们可以执行一些CRUD操作到数据库了,但我们的DAL还没有完成。我们仍然需要创建单元工作。单元工作将允许我们以操作性的方式将实体实际写入数据库。

构建单元工作模式

最后,我们来构建单元工作模式。如果存储库用于存储相关表的模型,那么可以将此视为数据库的对象模型,它保存了存储库。

添加新项 -> -> 命名为Database.cs

Creating Unit of Work

这是单元工作中的代码。请注意,它主要为我们提供了一个集中的类来访问存储库,我们可以从中理解其含义。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DOTS.Data
{
public class Database
{
private readonly DotsEntities _databaseContext = new DotsEntities();

public IRepository<map> Map
{
get { return new Repository<map>(_databaseContext); }
}

public IRepository<mapvalue> MapValue
{
get { return new Repository<mapvalue>(_databaseContext); }
}

public void Save()
{
_databaseContext.SaveChanges();
}
}
}

通过单元工作实现,我们可以轻松地进行CRUD操作。让我们看看这会是什么样子。这是我用来测试单元工作的一些代码。您可以看到与此模型一起工作是多么简单和直观。

DOTS.Data.Database database = new Database();

DOTS.Data.Map newMap = new Map();
newMap.name = "TEST";
newMap.writeQuestion = "What is my Dogs Name";
newMap.writeAnswer = "Fido";
newMap.created = DateTime.Now;
database.Map.Add(newMap);

database.Save();

数据访问层总结

至此,我们的数据访问层就完成了。现在我们可以进入通信层。我为构建通信层打下了很好的基础,所以在源代码中有一个工作的WebAPI项目,但我将在下一个挑战中稍作调整,并连接通信层。我还会将一个WinForms应用程序连接到我们的API,这将展示Azure虚拟机如何成为测试Windows应用程序的好地方。

项目开始成形,我学到了很多关于撰写这类文章的知识。希望您也能享受阅读和看到它成形的过程。

第四个挑战:虚拟机

通信层

  • Web API和Ajax

即将推出...

结束语

DOTS将给我带来许多独特的挑战。这个项目将是我第一次走出数据库领域,尝试编写通信方法或协议。通过这样做,我希望能够获得并分享关于构建网站和与云交互的现代技术的宝贵知识。

我还想感谢我的同事们,感谢他们提供的帮助,并帮助我构思了一个参加这次比赛的想法。

© . All rights reserved.