博客 (95)

发布选项 \ 项目类型Web 窗体网站

Web 应用程序

(Web 窗体)

Web 应用程序

(MVC)

ASP.NET Core 

Web 应用程序

在发布期间预编译

Precompile during publishing

若勾选,将 .cs 文件编译为 .dll
无论是否勾选,都将 .cs 文件编译为 .dll

允许更新预编译站点

Allow precompiled site to be updatable

若不允许,则会将 .aspx 等页面也一同编译,并以内容“这是预编译工具生成的标记文件,不应删除!”代替

未进行完整的测试和分析,总结有误请指正。

xoyozo 8 年前
6,221

502.5.png

解决方法:安装最新的 .NET Core runtime 即可:https://www.microsoft.com/net/download/core#/runtime

(Windows Server 上选择 Windows Server Hosting (x64 & x86))

xoyozo 8 年前
5,538
  1. 在 NuGet 中安装 Microsoft.AspNetCore.Session 和 Newtonsoft.Json

  2. 打开 Startup.cs,在 ConfigureServices 方法中加入(视情况配置相关属性)

    services.AddSession(options =>
    {
        // 设置了一个较短的时间,方便测试
        options.IdleTimeout = TimeSpan.FromSeconds(10);
        options.CookieHttpOnly = true;
    });
  3. 在 Configure 方法中加入

    app.UseSession();
  4. 添加 Extensions 文件夹并添加类 SessionExtensions.cs

    using Microsoft.AspNetCore.Http;
    using Newtonsoft.Json;
    
    public static class SessionExtensions
    {
        public static void Set<T>(this ISession session, string key, T value)
        {
            session.SetString(key, JsonConvert.SerializeObject(value));
        }
    
        public static T Get<T>(this ISession session, string key)
        {
            var value = session.GetString(key);
            return value == null ? default(T) :
                                  JsonConvert.DeserializeObject<T>(value);
        }
    }
  5. 使用方法

    HttpContext.Session.Set("abc", 123);
    var v1 = HttpContext.Session.Get<int>("abc");
    HttpContext.Session.Remove("abc");
    var v2 = HttpContext.Session.Get<int>("abc");
    // v1 = 123, v2 = 0
xoyozo 8 年前
5,285

问题:

    Invalid use of NULL value

原因:

    将字段从可空改为不可空时,数据库中存在该字段为空的行,因此更新列属性不成功。

解决:

    将数据库中已存在行中填充该字段值,再执行迁移。


问题:

    Duplicate column name 'xxx'

原因:

    数据库中已存在该列,但迁移希望执行 AddColumn,可能是手动修改了数据库结构导致的。

解决:

    删除数据库中该列(如果数据不重要),或通过删除迁移的方法回到数据库结构与 EF 快照一致的情况。


问题:

    Cannot add or update a child row: a foreign key constraint fails (`xxx`.`xxx`, CONSTRAINT `xxx` FOREIGN KEY (`xxx`) REFERENCES `xxx` (`xxx`) ON DELETE CASCADE)

原因:

    设置外键时,发现外键属性值不在导航属性所在表中。

解决:  

    添加外键属性和添加导航属性分两次迁移。

    第一次迁移:添加外键属性(字段),MySQL 可设外键属性非空,迁移成功后更改默认值 0 为真实值(即外键对应的主表中存在的值)。

    第二次迁移:添加导航属性(外键)。


xoyozo 8 年前
3,282

官方教程:Getting started with ASP.NET Core MVC and Entity Framework Core using Visual Studio


第一课:入门


Entity Framework Core NuGet 软件包


安装数据库提供程序(以 SQL Server 为例):其它数据库提供程序

Install-Package Microsoft.EntityFrameworkCore.SqlServer

该软件包依赖 Microsoft.EntityFrameworkCore 和 Microsoft.EntityFrameworkCore.Relational


新建数据模型


教程

课程注册 - 学生数据模型图

文件夹 Models 存放实体类


默认以 ID 或 类名ID 为主键列,并且是标识(即自动递增),以下代码可以取消标识,允许自定义 ID 值

[DatabaseGenerated(DatabaseGeneratedOption.None)]


导航属性:对于数据库来说就是外键,对一定义为实体类,对多定义为 ICollection<T>


字段类型可以是枚举 enum

创建数据库上下文

教程

在 Data 文件夹中创建一个名为 SchoolContext.cs 的类,继承于 DbContext

使用依赖注入注册上下文

教程


修改 Startup.cs 的 ConfigureServices 方法

在 appsettings.json 中配置数据库连接字符串

添加代码以使用测试数据初始化数据库

教程

在 Data 文件夹中创建一个名为 DbInitializer.cs 的类

修改 Startup.cs 的 Configure 方法

新建一个控制器和视图

在 Controllers 文件夹上右键,添加

MVC 依赖项选择 Minimal Dependencies 即可

会自动安装 NuGet 包:Microsoft.EntityFrameworkCore.Design 和 Microsoft.EntityFrameworkCore.SqlServer.Design

公约

表名优先来自 DbSet 属性名,其次是类名

实体属性名将映射为列名

实体属性名 ID 或 类名ID 将默认被设为主键

如果属性被命名,它将当作外键(例如,导航属性 Student 对应的实体 Student 的主键是 ID,所以 StudentID 是外键);


公约可以被覆盖,例如,可以指定表名,设置列名,并将任何属性设置为主键或外键。

异步代码

教程


第二课:新建、读取、更新和删除操作(CRUD)

自定义详细信息页面

Include() // 加载导航属性

ThenInclude() // 加载导航属性的导航属性

AsNoTracking() // 使该实体在当前上下文生命周期中不更新

SingleOrDefaultAsync() // 异步检索单个实体

路由数据

<a asp-action="Edit" asp-route-id="@item.ID">Edit</a>
<a asp-action="Edit" asp-route-studentID="@item.ID">Edit</a>

生成为

<a href="/Students/Edit/6">Edit</a>
<a href="/Students/Edit?studentID=6">Edit</a>

改进“新建页面”

在 HttpPost 的 Create 方法中,因为 ID 是自动递增的,所以可以从 Bind 中删除

[ValidateAntiForgeryToken] 搭配 if (ModelState.IsValid),用于实现验证,防止 CSRF 攻击

改进“删除页面”

在 HttpGet 的 Delete 方法中增加参数用于传递报错内容

在 HttpPost 的 Delete 方法中删除失败则 RedirectToAction 到 Delete 页面并带上报错参数

用”初始化和附加“的方法实现删除

Student studentToDelete = new Student() { ID = id };
_context.Entry(studentToDelete).State = EntityState.Deleted;

实例化要删除的实体(仅指定主键值),并将状态设置为 Deleted,即可在 SaveChanges 时实现删除,从而避免了删除必须先读取的情况。

处理交易

当 SaveChanges 时,EF 自动确保所有更改都成功或都失败,如果首先完成一些更改,然后发生错误,那么这些更改将自动回滚。

无追踪查询

什么情况下适用 AsNoTracking 方法:

在上下文中不需要更新任何实体,并且不需要加载导航属性(若需要导航属性,可以预先 Include 或稍后单独查询)

检索大量数据时,仅一小部分将被更新(稍后单独查询)

当前检索的实体需要在稍后以不同的目的附加(_context.Entry(*).State)


第三课:排序,过滤,分布和分组

添加列排序链接到学生索引页

教程

将“搜索框”添加到“学生索引”页面

教程

将分页功能添加到“学生索引”页面

教程

PaginatedList 类的使用

创建一个显示学生统计信息的关于页面

创建视图模型:在 Models 文件夹中创建一个 SchoolViewModels 文件夹,添加一个类文件 EnrollmentDateGroup.cs

修改 HomeController 添加上下文

使用 group by 进行分组查询


第四课:迁移


用于迁移的 NuGet 软件包

要使用迁移,可以使用包管理器控制台(PMC)或命令行界面(CLI)。教程

CLI 的另一种打开方式:在项目上右键 - 在文件资源管理器中打开文件夹,在地址栏上键入 cmd 回车


删除数据库的 CLI 命令

dotnet ef database drop

创建初始迁移

dotnet ef migrations add InitialCreate


创建数据库的代码位于 Migrations 文件夹中,Up 方法用于创建,Down 方法用于回滚

这时只生成了创建数据库的代码,并没有真正的创建数据库


多次创建的迁移会按时间戳的次序展示在 Migrations 文件夹中,数据库中记录了最后一次迁移的版本

检查数据模型快照

迁移会在 Migrations/SchoolContextModelSnapshot.cs 中创建当前数据库模式的快照,因此 EF Core 不必与数据库进行交互以创建迁移。

要删除最后一次迁移,执行:

dotnet ef migrations remove

将迁移应用于数据库

dotnet ef database update

执行完成会创建数据库结构,但表中的初始数据将在第一次运行时被添加


上次应用迁移之后创建的一个或多个新迁移,将在当前应用迁移时按顺序全部执行。


第五课:创建复杂的数据模型

实体图

通过使用属性自定义数据模型

[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]

DataType 用于指定比数据库固有类型更具体的数据类型,上例指定为日期,而不是日期和时间

DateType 支持:Date, Time, PhoneNumber, Currency, EmailAddress 等等

DateType 支持传达数据的语义到 HTML5

DisplayFormat 用于指定格式

ApplyFormatInEditMode 指定是否当值显示在文本框中进行编辑时也应用此格式


[StringLength(50)]
[StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")]
[RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]

StringLength 设置数据库中最大长度,并为客户端和服务端提供验证

StringLength 可以指定最小长度,但该值对数据库没有影响

StringLength 不会阻止用户输入空格,可以使用 RegularExpression 来限制

缩短最大长度会导致迁移时发出警告,为了保证顺利迁移,请先更改数据或重新选择合适的最大长度


[Column("FirstName")]
public string FirstMidName { get; set; }

上例将属性 FirstMidName 映射到数据库列名 FirstName

如何更改数据库中表的列名


[Required]

必需属性,不能为空的类型将自动处理必填字段,无需重复指定。


[Display(Name = "Last Name")]

显示属性,指定用于显示的名称


计算属性,只有一个 get 访问器,不会在数据库中生成列,例如:

public string FullName
{        
    get
    {
        return LastName + ", " + FirstMidName;
    }
}


可以将多个属性放在一行中:

[DataType(DataType.Date),Display(Name = "Hire Date"),DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]


在一对一的关系中,副表的主键名称不以副表实体 classnameID 命名,而以主表实体 classnameID 命名时,用 Key 属性来指定主键:

[Key]

或者需要以除 classnameID 或 ID 外的其它属性为主键,那么可以用 Key 


[Range(0, 5)]

指定数值范围


当有相关实体的导航属性时,EF 不要求在数据模型中添加外键属性。详情见教程


[DatabaseGenerated(DatabaseGeneratedOption.None)]

主键值由用户提供,而不是由数据库生成。


[Column(TypeName = "money")]
public decimal Budget { get; set; }

通常不需要列映射,此例将 decimal 类型的属性 Budget 映射为 money 类型的字段 Budget。


部门可能有负责人也可能没有,负责人必定是教师。因此 InstructorID 属性为教师实体的外键,并且可空,导航属性是被命名为 Administrator 的 Instructor 实体。

public int? InstructorID { get; set; }
public Instructor Administrator { get; set; }


通常,EF 允许对不可空的外键进行级联删除以久多对多关系。

例如,如果将 Department.InstructorID 属性定义为不可空,则 EF 将配置级联删除规则,以在删除部门时删除教师。如果我们想保留教师,但业务逻辑要求该 InstructorID 属性确实不可为空,则必须使用以下语句来禁用关系中的级联删除

modelBuilder.Entity<Department>()
   .HasOne(d => d.Administrator)
   .WithMany()
   .OnDelete(DeleteBehavior.Restrict)

补充,现在设计业务逻辑时会尽量避免删除操作,而使用禁用来代替,以在数据库中永久保留记录。


[DisplayFormat(NullDisplayText = "No grade")]

NullDisplayText 指定当值为 null 时的显示内容

联合主键

在多对多关系(教师到课程)的连接表(CourseAssignment)中,两个外键都不可为空,并且一同唯一标识表的每一行,因此不需要单独的主键,将 InstructorID 和 CourseID 作为一个联合主键。

定义联合主键不能通过使用属性来完成,唯一方法是使用 Fluent API,修改 Data/SchoolContext.cs 的 OnModelCreating 方法:

modelBuilder.Entity<CourseAssignment>().ToTable("CourseAssignment");
modelBuilder.Entity<CourseAssignment>()
    .HasKey(c => new { c.CourseID, c.InstructorID });

Fluent API 替代属性

protected override void OnModelCreating(ModelBuilder modelBuilder){
    modelBuilder.Entity<Blog>()
        .Property(b => b.Url)
        .IsRequired();
}

在本教程中,您只使用 Fluent API 进行数据库映射,而不能使用属性。您还可以使用 Fluent API 来指定可以使用属性执行的大多数格式化、验证和映射规则。一些属性(如 MinimumLength)不能应用 Fluent API,因为它不会更改数据库模式,它只适用于客户端和服务端验证。

一些开发人员喜欢专门使用 Fluent API,以保持实体类“干净”。当然可以混合使用属性和 Fluent API,但推荐只选择一种方式。如果混合使用,那么 Fluent API 会覆盖属性。

添加迁移

dotnet ef migrations add ComplexDataModel

如果在此时尝试运行命令 database update(不要这样做),您将收到以下错误:

ALTER TABLE语句与FOREIGN KEY约束“FK_dbo.Course_dbo.Department_DepartmentID”冲突。冲突发生在数据库“ContosoUniversity”,表“dbo.Department”,“DepartmentID”列。

这是因为在课程 Course 表增加了一个 DepartmentID 外键,而且是不可空的,又由于课程表中已经存在数据,所以无法 AddColumn。

我们可以创建一个临时部门,然后将它的 DepartmentID 值赋给 Course.DepartmentID 作为默认值。

打开 _ComplexDataModel.cs 文件,注释将 DepartmentID 添加到课程表的代码行,注释之,并在创建 Department 表的代码之后添加创建默认部门和添加 DepartmentID 的代码,详见教程


第六课:读取相关数据

预先加载、显式加载和延时加载

教程

  • 预先加载:当实体被读取时,相关数据随之被检索。即在单个连接查询中读取所有所需数据。通过 Include 和 ThenInclude 方法实现。

  • 显式加载:当实体首次读取时,不检索相关数据。如果需要再检索相关数据。显式加载会导致多次数据库查询。

  • 延时加载:当实体首次读取时,不检索相关数据。在首次尝试访问导航属性时,将自动检索该导航属性所需的数据。

* 从定义上看“显示加载”和“延时加载”比较类似,详细区别会在学完本课后有具体的了解。

创建一个显示部门名称的课程页面

教程

在课程列表中,每个课程都显示部门名称,因此使用预先加载。

在控制器中使用 Include 和 AsNoTracking

在视图中使用 @Html.DisplayFor(modelItem => item.Department.Name)

创建一个显示课程和报名的教师页面

教程

为教师页面创建一个视图模型 InstructorIndexData.cs

在控制器中多次使用 Include 和 ThenInclude 来预先加载相关数据

第一个 Include 加载办公室(一对零或一)

第二个 Include 加载课程(多对多)和报名课程的学生(多对多)

第三个 Include 加载课程(多对多)和课程所在部门(多对一)

本例中“课程的学生”和“课程的部门”是各自级联 ThenInclude 加载的,不能合并到同一个 Include 的课程中,因为 ThenInclude 不能读取多个分支的导航属性,每个分支都必须使用 Include 从教师实体调用。

在视图中给选择的教师添加 class 样式类

显式加载

教程

去掉 Include 和 AsNoTracking,然后在需要的时候附加相关的实体(上下文.Entry())来实现显式加载,以减少首次读取实体时的数据量。

await 上下文.Entry(单个实体).Collection(x=>x.导航属性).LoadAsync(); // 一对多

await 上下文.Entry(单个实体).Reference(x=>x.导航属性).LoadAsync(); // 一对一


第七课:更新相关数据

课程必须与部门关联,在新建和编辑课程时提供部门的下拉列表。

ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
<select asp-for="DepartmentID" class="form-control" asp-items="ViewBag.DepartmentID">
    <option value="">-- Select Department --</option>
</select>

SelectList 为下拉列表创建一个集合,允许指定默认选中项,并传递到 ViewBag。


将 .AsNoTracking() 添加到详细信息和删除方法


“一对零或一”关系:(本例是教师和办公室分配,抑或是文章和正文、学生和档案)

删除教师的办公室分配时,如果原先具有值,则删除办公室分配;

输入教师的办公室分配时,如果原先为空,则创建一个办公室分配;

更改教师的办公室分配时,更改现在有办公室分配值。

如何处理:

在 HttpGet 的 Edit 方法中,Include 办公室实体,并 AsNoTracking

在 HttpPost 的 Edit 方法中,移除除 id 以外的参数(为了不与 HttpGet 的 Edit 冲突,改名为 EditPost,并 ActionName("Edit"))

if (await TryUpdateModelAsync<Instructor>(
     instructorToUpdate,
     "",
     i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))

使用模型绑定器的值更新检索到的教师实体。TryUpdateModel 支持更新实体的导航属性。

我们可以直接设置实体的导航属性为 null,以便相关的行将被删除。


第八课:处理并发冲突

悲观并发,即锁定,性能差。

乐观并发:

[Timestamp]
public byte[] RowVersion { get; set; }

添加一个 timestamp 类型的 RowVersion 字段

详见教程


第九课:继承

教程


第十课:进阶专题

调用“返回实体”的查询

为了防止 SQL 注入,使用参数化查询:

public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }
    
    string query = "SELECT * FROM Department WHERE DepartmentID = {0}";
    
    var department = await _context.Departments
        .FromSql(query, id)
        .Include(d => d.Administrator)
        .AsNoTracking()
        .SingleOrDefaultAsync();
        
    if (department == null)
    {
        return NotFound();
    }
        
    
    return View(department);
}

调用“返回其他类型”的查询

编写 SQL,而非 LINQ:

public async Task<ActionResult> About()
{
    List<EnrollmentDateGroup> groups = new List<EnrollmentDateGroup>();
    var conn = _context.Database.GetDbConnection();
    try
    {
        await conn.OpenAsync();
        using (var command = conn.CreateCommand())
        {
            string query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
                + "FROM Person "
                + "WHERE Discriminator = 'Student' "
                + "GROUP BY EnrollmentDate";
            command.CommandText = query;
            DbDataReader reader = await command.ExecuteReaderAsync();

            if (reader.HasRows)
            {
                while (await reader.ReadAsync())
                {
                    var row = new EnrollmentDateGroup { EnrollmentDate = reader.GetDateTime(0), StudentCount = reader.GetInt32(1) };
                    groups.Add(row);
                }
            }
            reader.Dispose();
        }
    }
    finally
    {
        conn.Close();
    }
    return View(groups);
}

调用“更新”查询(批量更新)

使用参数化查询批量更新数据,返回受影响行数:

ViewData["RowsAffected"] = 
    await _context.Database.ExecuteSqlCommandAsync(
        "UPDATE Course SET Credits = Credits * {0}",
        parameters: multiplier);

检查发送到数据库的 SQL

有时,可以看到发送到数据库的实际 SQL 查询很有帮助。

第一步:设置一个断点

第二步:调试模式运行程序

第三步:在“输出”窗口可以看到 SQL 查询语句

自动变化检测

EF 通过将实体的当前值与原始值进行比较来确定实体如何更改。

一些方法会导致自动更改检测:

DbContext.SaveChages

DbContext.Entry

ChangeTracker.Entries

如果您正在跟踪大量实体,并且您在循环中多次调用上述方法之一,则可以通过临时关闭 AutoDetectChangesEnabled 属性,能显著提升性能。

_context.ChangeTracker.AutoDetectChangesEnabled = false;

从现有数据库反向工程

完整教程

常见错误

问:ContosoUniversity.dll 由另一个进程使用

答:在 IIS Express 中停止站点。

问:迁移在 Up 和 Down 方法中没有代码

原因:在运行migrations add命令时有未保存的更改

答:运行migrations remove命令,保存代码更改并重新运行migrations add命令。




xoyozo 8 年前
5,735

如果我们直接更改模型的属性名,在迁移的时候会报错:

Data\DbInitializer.cs(,): error CS0117: '表' does not contain a definition for '新列名'


原因是它无法判断是把什么列名更改为什么列名(比如一次改多列呢),那么我们需要用 [Column("列名")] 来映射


方法一:

先加上 [Column("新列名")] ,执行迁移

再修改模型的属性名并删除 [Column("新列名")],执行迁移


方法二:

先加上 [Column("原列名")] ,同时修改模型的属性名,执行迁移,

再删除 [Column("原列名")],执行迁移


xoyozo 8 年前
5,144

本文适用于 EF Core,并非 EF 6.x 及以下。

在本演练中,您将构建一个使用 Entity Framework 执行基本数据访问的 ASP.NET Core MVC 应用程序。您将使用逆向工程基于现有数据库创建实体框架模型。

创建一个新项目

  • 打开 Visual Studio 2017

  • 文件 -> 新建 -> 项目...

  • 从左侧菜单中选择 已安装 -> 模板 -> Visual C# -> Web

  • 选择 ASP.NET Core Web应用程序(.NET Core)项目模板

  • 输入项目名称,然后单击确定

  • ASP.NET Core 1.1 选择 Web 应用程序

  • 确保“身份验证”设置为“不进行身份验证

  • 点击确定

安装 Entity Framework

要使用 EF Core,请安装目标数据库提供程序的软件包。本演练使用 SQL Server。有关可用提供程序的列表,请参阅 Database Providers(从打开页面的左侧菜单选择相应的数据库)。

  • 工具 -> NuGet 包管理器 -> 管理解决方案的 NuGet 程序包

  • 在“浏览”选项卡中搜索并安装 Microsoft.EntityFrameworkCore.SqlServer

我们将使用一些 Entity Framework 命令从数据库创建模型。所以我们也将安装工具包。

  • 安装 Microsoft.EntityFrameworkCore.SqlServer.Design

  • 安装 Microsoft.EntityFrameworkCore.Tools

模型的反向工程

现在是根据现有数据库创建 EF 模型的时候了。

  • 工具 -> NuGet 包管理器 -> 程序包管理器控制台

  • 运行 Scaffold-DbContext "数据库连接字符串" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models

数据库连接字符串可以从 服务器资源管理器 -> 数据连接,在要连接的数据库上右击属性来获得。

如果您收到以下错误说明,请关闭并重新打开 Visual Studio 后重试。

The term 'Scaffold-DbContext' is not recognized as the name of a cmdlet

逆向工程过程基于现有数据库的模式创建实体类(表名.cs)和派生上下文(库名Context.cs)。

实体类是简单的 C# 对象,表示要查询和保存的数据。

上下文表示与数据库的会话,并允许您查询和保存实体类的实例。

使用依赖注入注册上下文

依赖注入的概念是ASP.NET Core的核心。服务(例如数据库 Context)在应用程序启动期间使用依赖注入进行注册。然后,需要这些服务的组件(例如您的 MVC 控制器)通过构造函数参数或属性提供这些服务。

删除内联上下文配置

  • 打开 Models\库名Context.cs

  • 删除方法 OnConfiguring(...)

  • 添加以下构造函数,这将允许通过依赖注入将配置传递到上下文中

public 库名Context(DbContextOptions<库名Context> options)
    : base(options)
{ }

在 Startup.cs 中注册和配置上下文

为了使我们的 MVC 控制器使用数据库Context,我们将它注册为一个服务。

  • 打开 Startup.cs

  • 使用 using 语句在文件的开头添加以下内容

using 项目命名空间.Models;
using Microsoft.EntityFrameworkCore;

现在我们可以使用 AddDbContext(...)方法将其注册为服务。

  • 找到 ConfigureServices(...)方法

  • 添加以下代码以将上下文注册为服务

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddMvc();

    var connection = @"连接字符串";
    services.AddDbContext<库名Context>(options => options.UseSqlServer(connection));
}

在实际应用程序中,通常将连接字符串放在配置文件中。为了简单起见,我们在代码中定义它。查看如何将连接字符串配置到 appsettings.json 文件中

创建一个控制器

接下来,我们将在我们的项目中启用基架。

  • 右键单击解决方案资源管理器中的 Controllers 文件夹,然后选择添加 -> 控制器...

  • 选择 Full Dependencies(完全依赖),然后单击添加

  • 您可以忽略打开的 ScaffoldingReadMe.txt 文件中的指令

现在基架已启用,我们可以为数据表实体构建控制器。

  • 右键单击解决方案资源管理器中的 Controllers 文件夹,然后选择添加 -> 控制器...

  • 选择“视图使用 Entity Framework 的 MVC 控制器”并单击“添加

  • 设置模型类数据上下文类

  • 单击添加

运行应用程序

您现在可以运行应用程序以查看它的操作。

  • 调试 -> 开始执行(不调试)

  • 应用程序将在 Web 浏览器中构建和打开

  • 导航到相应的路径 /控制器名

  • 单击 Create New

  • 填写表单并单击 Create

EF Core for MySQL

  • 截至 Visual Studio 2017 正式版发布(2017年3月7日),MySQL for Visual Studio支持)尚不支持 Visual Studio 2017,因此 Visual Studio 2017 的服务器资源管理器不能连接数据源为 MySQL 的数据库,也无法创建和更新可视化模型(.edmx)

  • NuGet 包 MySql.Data.EntityFrameworkCore 还没有找到 Database First 的 PM 命令,因此不能由 MySQL 数据库生成实体类和上下文

  • 综上,要连接 MySQL 只能用 VS2015,要生成 MySQL 实体类和上下文就不能用 EF Core,暂时使用 EF 6.x 代替(创建 ASP.NET MVC 项目)

EF Core 1.1 未实现

  • EF Core 1.1 未实现:可视化模型,以查看基于代码的模型的图形表示。

  • EF Core 1.1 未实现:用于逆向工程的 Visual Studio 向导,允许您在从现有数据库创建模型时可视化地配置连接,选择表等。

  • EF Core 1.1 未实现:从数据库更新模型,允许从数据库以前反向工程的模型使用对模式所做的更改进行刷新。

补充

如果我们在创建新项目时选择 ASP.NET Core Web应用程序(.NET Framework项目模板,那么完全按照 Core 的方式来开始(比如选择 NuGet 包的版本),而发布必须是 .NET Framework 环境,而非跨平台。

参考

ASP.NET Core - Existing Database

Compare EF Core & EF6.x

xoyozo 9 年前
10,384

程序设计规范:


  • 【推荐】上传的文件直接保存到文件存储服务(如阿里云 OSS),这样即使被上传了后门 shell,对网站服务器也不会有影响。

  • 否则必须通过文件头来确定文件类型,检查文件十六进制的文件头和文件尾是否合法,并检查文件流中是否包含 php、evel 等字符。

  • 不可直接使用客户端文件名来保存文件,特别是后缀名/扩展名。应生成随机文件名,并通过检验文件头来确定文件类型。必须由程序指定保存目录。

  • 使用 OSS 的应直接上传,不要在 ECS 上临时存放或备份。如必须存放的,应按上述规范操作。


服务器安全设置


CentOS + nginx + PHP:

  • 全站文件取消属性中的“执行”权限(chmod),因为这个“执行”与运行 PHP 无关。而需要上传文件的“目录”需要“执行”权限,原因是需要往该目录创建文件。

  • 仅需要写入的目录或文件设置“写入”权限。如上传图片目录、ThinkPHP 的 Runtime 目录。

  • 凡可写目录或文件均不允许运行 PHP / PY 等 除需要被直接访问的 PHP / PY 文件,其它动态文件均不允许被访问到,在 nginx 的配置文件中添加项,参:https://xoyozo.net/Blog/Details/nginx-location-if,若全站使用统一的入口文件访问,那么设置仅该文件允许运行 PHP 即可。通过 IO 方式被其它文件包含的文件,无需运行 PHP 权限。(“deny all”对 include 起作用,但对 IO 不起作用,因此 Runtime 目录可以继续为 ThinkPHP 提供缓存服务。)这一步非常有用。

  • 使用与 nginx 网站用户不同的用户来部署网站文件,如宝塔面板 PHP 使用 www 用户,那么就使用 root 或其它新用户来上传文件,否则将导致全站目录和文件可写。有条件的建议不同网站使用不同的用户,可防止一个网站被入侵后导致其它网站文件或磁盘上的其它文件被泄露的风险(2022年10月2日从宝塔官方社区获悉,宝塔面板暂不支持使用非 www 用户创建并运行网站)。


Windows Server + IIS + ASP.NET:

  • 配置每个磁盘的安全属性,拒绝“IIS_IUSRS”这个用户组的所有权限。只要设置驱动器即可,子文件夹和文件会自动继承。若运行 .NET Framework 项目,需要设置 C:\Windows\Microsoft.NET\Framework\v*.*.*****\Temporary ASP.NET Files\ 目录可修改写入权限,.NET Core 项目不需要此设置。

  • 为每个网站创建一个新用户,仅隶属于“IIS_IUSRS”。网站根目录安全属性添加该用户,权限选择“读取”。(已测取消“读取与执行”不影响 PHP,“列出文件夹内容”视业务需求开启,建议关闭)。仅需要上传文件的目录或文件设置“修改”、“写入”权限。(修改对应修改文件,写入对应上传文件)

  • IIS 网站中设置“物理路径凭据”以及应用程序池的“标识”。

  • IIS 中设置写入目录的“处理程序映射”无脚本

xoyozo 10 年前
6,431

了解使用 Visual Studio 创建的模板项目

启动 VS,从菜单项打开:文件 - 新建 - 项目,在已安装的模板中选择 .NET Core,选择 ASP.NET Core Web Application(.NET Core),给项目取个名称,确定。选择 Web 应用程序,确定。

· global.json: 你可以在这里放置解决方案的配置信息和工程之间的引用。作用类似于 *.sln 文件。

· Program.cs: 这个文件包含了 ASP.NET Core 应用的 Main 方法,负责配置和启动应用程序。

· src 文件夹: 包含组成你应用程序的全部项目代码。

· wwwroot: 你的静态文件将被放置在这个文件夹,它们都将作为资源直接提供给客户端,包含 HTML,CSS 和 JavaScript 文件。可以在 project.json 内重置。

· project.json: 包含项目设置。在 ASP.NET Core 中,你可以通过使用 NuGet 程序包管理工具(NPM)添加 NuGet 包或者编辑这个文件来管理从属。

· project.lock.json: 由 project.json 生成。

· Startup.cs 这个主要放置你 ASP.NET Core 的 stratup 和 configuration 代码。ConfigureServices 方法定义了你应用程序使用的服务,Configure 方法用来定义组成请求管道的中间件。

一些备注:

ASP.NET Core 项目中不能创建 Web 窗体页面(.aspx)

发布后的网站根目录是项目中的 /wwwroot 目录

路由在 Startup 的 Configure 方法中配置

xoyozo 9 年前
7,463