在ASP.NET网页中使用实体框架代码和JSON

如果要使用ASP.NET网页构建Ajax供电的富客户端接口,您将使用JSON - 很多。您也可能被数据访问技术的实体框架吸引。在序列化实体框架今天福彩字谜总汇以Web页面应用程序中使用时,存在一些特殊的注意事项。本文探讨了您将遇到的一些问题,通过简单待办事项Manager的示例。

示例应用程序非常非常简单:它显示来自A待办事项列表的项目,并提供添加新项目的方法。今天福彩字谜总汇模型也很简单。待办事项列表中的每个项目都以代码为任务今天福彩字谜总汇实现。每个任务都会导致活动定义为TaskType今天福彩字谜总汇。此练习遵循使用实体框架的代码首先方法。如果您不熟悉代码以及如何将实体框架添加到您的网站,则可能需要阅读我的 上一篇文章涵盖了主题。假设您与基础知识讨论,这里是此应用程序的实体的代码。首先,任务类:

using System;
using System.ComponentModel.DataAnnotations;

/// <summary>
/// Represents an item in a TO DO list
/// </summary>
public class  任务 
{
    [Required]
    public int  任务 Id { get; set; }
    [Required, MaxLength(100)]
    public string  任务 Title { get; set; }
    [Required, MaxLength()]
    public string Details { get; set; }
    [Required]
    public DateTime  完全  { get; set; }
    public bool IsDone { get; set; }
    public int  TaskTypee. e.Id { get; set; }
    public virtual  TaskTypee. e.  TaskTypee. e. { get; set; }
}

现在TaskType类:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

/// <summary>
/// Represents the expected output of a task 
/// </summary>
public class  TaskTypee. e.
{
    [Required]
    public int  TaskTypee. e.Id { get; set; }
    [Required, MaxLength(50)]
    public string Activity { get; set; }
    public virtual ICollection< 任务 > Tasks { get; set; }
}

这两个都在App_Code文件夹中放置了一个单独的文件(任务和TaskType.cs),以便在应用程序首次运行时编译它们。此外,需要从DBContext中获取的类:

using System.Data.Entity;

/// <summary>
/// Enables data access for entities
/// </summary>
public class  TaskContext.  : DbContext
{
    public DbSet< 任务 > Tasks { get; set; }
    public DbSet< TaskTypee. e.> TaskTypes { get; set; }
}

dbsets. 将成为EF和实例生成的数据库中的表 TaskContext. 将提供使用C#代码而不是SQL对数据库执行SQL的手段。 C#代码将基于存储库模式,数据访问方法由实体分组。这 任务 Repository. code is as follows:

using System;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// Centralises data operations for Task objects
/// </summary>
public class  任务 Repository.
{
     TaskContext.  db =  新的   TaskContext. ();

    public  任务  Save( 任务  task) {
        task.TaskType = db.TaskTypes.Find(task.TaskTypeId);
        if (task.TaskId == 0) {
            db.Tasks.Add(task);
        }
        else {
            var t = db.Tasks.Find(task.TaskId);
            if(task.IsDone){
                t.IsDone = task.IsDone;
            } else {
                t.TaskTitle = task.TaskTitle;
                t.Details = task.Details;
                t.CompleteBy = task.CompleteBy;
                t.TaskType = task.TaskType;
            }
        }
        db.SaveChanges();
        return db.Tasks.Find(task.TaskId);
    }



    public 列表< 任务 > GetAllTasks(){
        return db.Tasks.ToList();
    }
}

有两种方法。第一个是保存方法,它涵盖了插入和更新。它接受任务obejct作为参数。如果该任务今天福彩字谜总汇没有TaskID,则尚未创建,因此该任务将作为任务DBSET的新条目添加为新条目。如果有任务ID,则存在该今天福彩字谜总汇,需要修改。如果ISDONE是真的,那么被认为是所需的唯一变革。否则,修改任务属性。最后,将更改致力于数据库,返回新的或修改的任务今天福彩字谜总汇。第二种方法返回返回作为通用列表的所有任务。

任务 TyPerePhository代码包含一个方法,将所有TaskTytype今天福彩字谜总汇返回为列表:

using System;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// Centralises data operations for TaskType objects
/// </summary>
public class  TaskTypee. e.Repository
{
     TaskContext.  db =  新的   TaskContext. ();
    
    public 列表< TaskTypee. e.> GetAllTaskTypes(){
        return db.TaskTypes.ToList();
    }
}

这些方法将由一组服务使用,这将负责将存储库生成的实体转换为JSON,以及将调用页面生成的JSON转换为存储库方法可以使用的实体今天福彩字谜总汇。每个服务都将占用自己的.cshtml文件。因此,我创建一个名为jsonservice的文件夹,并将三个文件添加到它:getalltakytaktypes.chtml,getalltasks.cshtml和savetask.cshtml。 getAlltasktypes.chtml包含以下代码:

@{
    var repository =  新的   TaskTypee. e.Repository();
    var taskTypeList = repository.GetAllTaskTypes().Select(t =>  新的  {
        TaskTypeId = t.TaskTypeId,
        Activity = t.Activity
    });
    Json.Write(taskTypeList, Response.Output);
}

该服务使用JSON帮助程序生成JSON,但它不会序列性 列表<TaskType> 存储库返回。列表<TaskType> 被转换成一个 匿名类型 这就是春天的序言。您可以看到LINQ选择扩展方法已添加到存储库方法调用中,并且Lambda表达式在序列中取出每个元素并将其转换为不同类型。但是,在此之后没有指定类型 新的 关键字,所以编译器在飞行中生成一个类型。匿名类型通常用作只读属性值的便利容器。

为什么转换这一点 TaskTypee. e. 收集到匿名类型为ALLT?好吧,每一个人 TaskTypee. e. 今天福彩字谜总汇有一个参考 任务 集合,每个 任务 今天福彩字谜总汇有一个参考 TaskTypee. e. 目的。 JSON帮助程序中的Serialiser无法应对产生的循环参考,如果尝试序列性 TaskTypee. e. 今天福彩字谜总汇,或者 任务 今天福彩字谜总汇,抛出异常:

例外细节: System.InvalidOperationException:在序列化“system.data.entity.dynamicproxies类型的今天福彩字谜总汇时检测到循环引用。<T>(其中T是序列化的类型。)

有几种方法可以防止这种情况,但最简单的是"flatten"数据,从而删除它在序列化之前的任何引用。这是通过将其投入新形式来实现的,匿名类型是完美的。使用匿名类型的相同方法是在getAllttasks.cshtml代码中获取:

@{
    var repository =  新的   任务 Repository.();
    var taskList = repository.GetAllTasks().Select(t =>  新的  {
        TaskId = t.TaskId,
        TaskTitle = t.TaskTitle,
        Details = t.Details,
        CompleteBy = t.CompleteBy.ToString("yyyy/MM/dd"),
        IsDone = t.IsDone,
        Activity = t.TaskType.Activity
    });
    Json.Write(taskList, Response.Output);
}

任务今天福彩字谜总汇的 完全 属性是DateTime数据类型。独自一人,它将以特定的格式序列性,EGE: / /日期(1343430000000)\ / 数字代表自1970年1月1日以来的经过毫秒。这将需要在客户端代码中进行一些操作,以使其显示为可识别的日期。如果我正在将JSON暴露给第三方,我可能只是允许JSON帮助程序执行它的事情,让其他人担心如何显示日期。但是,因为只有内部使用,我选择将值转换为服务器上的字符串,然后序列化。它可以保存必须构建一些额外的JavaScript来格式化浏览器中的值。

决赛"service"对于此应用程序是负责拍摄新的或修改的任务并将其保存到数据库的应用程序,这是SAVetask.chml的责任:

@{
    var reader =  新的  StreamReader(Request.InputStream);
    var  杰森  = reader.ReadToEnd();
    var task = Json.Decode< 任务 >(json);
    var repository =  新的   任务 Repository.();
    var t = repository.Save(task);
    var savedTask =  新的  {
        TaskId = t.TaskId,
        TaskTitle = t.TaskTitle,
        Details = t.Details,
        CompleteBy = t.CompleteBy.ToShortDateString(),
        IsDone = t.IsDone,
        Activity = t.TaskType.Activity
    };
    Json.Write(savedTask, Response.Output);
}

有许多方法可以通过AJAX将今天福彩字谜总汇从客户端JavaScript传输到服务器。第一个是使用HTML形式序列,使用 邮政 。您可以从中获取值 请求[form-field-name] 在服务器上。您还可以使用查询字符串中的一堆名称/值对 得到 ,也可以通过 请求[form-field-name]。或者您可以构建JSON今天福彩字谜总汇并将其发送给它 身体 A. 邮政 要求。如果您这样做,则该值可用 InputStream. 财产的财产 要求 目的。这是SAVetask服务如何希望任务今天福彩字谜总汇到达 - 在POST请求的正文中。该值存储在调用的变量中 杰森 一旦从中提取 InputStream. ,然后使用强类型转换为C#任务obejct。 json.decode. <T> 方法,在哪里 T 表示解码JSON进入的类型。该任务今天福彩字谜总汇通过了存储库的保存方法,该方法将添加新任务或修改和现有一个,然后将新的或已修改的任务返回给服务。为序列化准备并发送回客户端。

所以回顾 - 我们有我们定义的今天福彩字谜总汇 - 任务和TaskType。我们有一种方法可以通过我们的TaskContext今天福彩字谜总汇从DBContext映射到数据库表中的方法。我们在存储库类中有一些简单的数据访问方法,我们有一种方法可以使用来自客户端JavaScript的这些数据访问方法。我们实际上并不是有数据库,我们都没有使用任何客户端代码来显示或添加新的任务今天福彩字谜总汇。我们将首先处理缺乏数据库。

此示例不会提供从用户界面创建TaskTypes的方法,因此将从代码中创建。代码的一个功能首先是数据库不是生成的,直到代码attemtps才能使用它,所以我们可以通过添加一些代码和appstart文件来使用一块石头杀死两只鸟类:

@using EF = System.Data.Entity;
@{
    EF.Database.SetInitializer( 新的  EF.DropCreateDatabaseIfModelChanges< TaskContext. >());
    var db =  新的   TaskContext. ();
    db.TaskTypes.Add( 新的   TaskTypee. e. {Activity = "Email"});
    db.TaskTypes.Add( 新的   TaskTypee. e. {Activity = "Meeting"});
    db.TaskTypes.Add( 新的   TaskTypee. e. {Activity = "Document"});
    db.TaskTypes.Add( 新的   TaskTypee. e. {Activity = "Phone Call"});
    db.TaskTypes.Add( 新的   TaskTypee. e. { Activity = "Letter" });
    db.SaveChanges();
}

AppStart在网站首次启动时执行。因此,此代码将导致正在创建的数据库以及添加到TaskTyType表中的一系列任务类型。如果网站重新启动,则 TaskTypee. e.s 将再次添加,因此您应该记得删除一旦创建数据库并填充了此代码。

这只是离开用户界面,可以在单个页面中容纳。在这里它完全:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>TODO Manager</title>
        <link href="~/Content/site.css" rel="stylesheet" type="text/css" />
        <link href="~/Content/jquery-ui-1.8.21.custom.css" rel="stylesheet" type="text/css" />
        <script src="~/Scripts/jquery-1.7.2.min.js" type="text/javascript"></script>
        <script src="~/Scripts/jquery-ui-1.8.21.custom.min.js" type="text/javascript"></script>
        <script type="text/javascript">
            //Controls appearance of new tasks
            function slide(panel, content){
                $('#' + panel).slideDown(600, function(){
                    $('#' + content).animate(
                        {
                            opacity: '1'
                        },  600
                    );
                });
            }

            $(function () {
                // Prevent browser caching
                $.ajaxSetup({ cache: false });
                
                var taskList = $('#tasks');
                var select = $('#TaskTypeId');

                //Get all tasks and display them
                $.getJSON('/JsonService/GetAllTasks', function(tasks) {
                    $.each(tasks, function(index, task) {
                        var panel = 'panel' + task.TaskId;
                        var content = 'content' + task.TaskId;
                        var html = '<div id=\'' + panel + '\' class=\'panel\'><div id=\'' + content + '\' class=\'content\'>';
                        html += '<p><strong>' + task.TaskTitle + '</strong></p>';
                        html += task.Details + '<br />';
                        html += 'Complete By: ' + task.CompleteBy + '<br />';
                        html += task.Activity + '</div></div>';
                        taskList.prepend(html);
                        slide(panel, content);
                    });
                });

                // Populate the dropdwnlist with Task Types
                $.getJSON('/JsonService/GetAllTaskTypes', function(taskTypes) {
                    select.empty();
                    select.append($('<option/>').attr('value', '').text('--Choose Task Type--'))
                    $.each(taskTypes, function(index, taskType) {
                        select.append($('<option/>').attr('value', taskType.TaskTypeId).text(taskType.Activity));
                    });
                });

                // Set up the date picker
                $('#CompleteBy').datepicker({ dateFormat: 'yy/mm/dd' })

                // Open the modal form on the button click 
                $('#add-task').click(function() {
                    $('#new-task').dialog('open');
                });

                // Convert the div to a modal dialog form
                $('#new-task').dialog({
                    autoOpen: false,
                    modal: true,
                    height: 315,
                    width: 500,
                    buttons: {
                        'Add Task': function () {
                            // Create a new task object
                            var task = {
                                TaskTitle: $('#TaskTitle').val(),
                                Details: $('#Details').val(),
                                CompleteBy: $('#CompleteBy').val(),
                                TaskTypeId: $('#TaskTypeId').val()
                            };
                            // Add the new task and display it
                            $.ajax("JsonService/SaveTask", {
                                data: JSON.stringify(task),
                                type: "post",
                                contentType: "application/json",
                                success: function(data) {
                                    var savedTask = JSON.parse(data);
                                    var panel = 'panel' + savedTask.TaskId;
                                    var content = 'content' + savedTask.TaskId;
                                    $('#new-task').dialog('close');
                                    var html = '<div id=\'' + panel + '\' class=\'panel\'><div id=\'';
                                    html += content + '\' class=\'content\'>';
                                    html += '<p><strong>' + savedTask.TaskTitle + '</strong></p>';
                                    html += savedTask.Details + '<br />';
                                    html += 'Complete By: ' + savedTask.CompleteBy + '<br />';
                                    html += savedTask.Activity + '</div></div>';
                                    taskList.prepend(html);
                                    slide(panel, content);
                                    $('#TaskTitle').val('');
                                    $('#Details').val('');
                                    $('#CompleteBy').val('');
                                    $('#TaskTypeId').val('');
                                }
                            });
                        },
                        Cancel: function() {
                            $('#new-task').dialog('close');
                        }
                    }
                });
            });
        </script>
    </head>
    < 身体 >
        <button id="add-task">Add Task</button>
        <div id="tasks"></div>
        <div id="new-task" title="New Task">
            <div class="editor-row">
                <span class="editor-label">Title:</span>
                <span class="editor-field">@Html.TextBox("TaskTitle", null,  新的  {size = 40})</span>
            </div>
            <div class="editor-row">
                <span class="editor-label">Details:</span>
                <span class="editor-field">@Html.TextArea("Details",  新的  {rows = 6, cols = 50})</span>
            </div>
            <div class="editor-row">
                <span class="editor-label">Complete By:</span>
                <span class="editor-field">@Html.TextBox("CompleteBy")</span>
            </div>
            <div class="editor-row">
                <span class="editor-label"> 任务  Type:</span>
                <span class="editor-field">@html.dropdownList.("TaskTypeId", Enumerable.Empty<SelectListItem>())</span>
            </div>
        </div>
    </ 身体 >
</html>

从底部开始,您可以看到一个按钮,空DIV和一系列表单字段,但没有表单元素。空的div叫 任务 用于显示任务列表,然后使用该按钮调用jQuery对话框,WHCIH将显示表单字段。使用任务类型选择器使用 html.dropdownList. 帮手。它是一个空集合的预制。当页面加载时,jQuery getjson. 调用方法,可从GetAllTask​​types.chtml获取TaskType数据,并与它们填充下拉列表。另一个Getjson调用是拒绝获取任何现有任务,并将其置于空DIV中。完成对话框字段并单击“保存”按钮时,从输入中创建JavaScript任务今天福彩字谜总汇,然后使用json使用 杰森 .stringify. (浏览器方法,无特定于jQuery)并发布到SaveTask.cshtml服务。返回值被添加到已出现的任何位置,并且jQuery动画的一些jQuery动画将jazz jaze jaze。你可以 克隆github repo,它具有本文中的代码 并在你喜欢的时候试验。设置它准备好创建数据库并在第一次运行时填充TaskType。

概括

本文所涵盖的关键概念是:如何基于存储库模式设计实体框架数据访问;需要在序列化之前达成实体框架今天福彩字谜总汇;如何使用 杰森 .stringify() 将JavaScript今天福彩字谜总汇转换为JSON;如何在请求身体中传输JSON并通过访问它 要求 .InputStream.;和使用 json.decode. <T> 将JSON转换为C#代码理解并可以使用的类型。