ASP.NET Core 入門教程 8、ASP.NET Core + Entity Framework Core 數據訪問入門

来源:https://www.cnblogs.com/ken-io/archive/2018/12/29/aspnet-core-tutorial-entity-framework-core-mysql.html
-Advertisement-
Play Games

一、前言 1、本教程主要內容 ASP.NET Core MVC 集成 EF Core 介紹&操作步驟 ASP.NET Core MVC 使用 EF Core + Linq to Entity 訪問MySQL資料庫 ASP.NET Core MVC 使用 EF Core + 原生SQL訪問MySql數 ...


一、前言

1、本教程主要內容

  • ASP.NET Core MVC 集成 EF Core 介紹&操作步驟
  • ASP.NET Core MVC 使用 EF Core + Linq to Entity 訪問MySQL資料庫
  • ASP.NET Core MVC 使用 EF Core + 原生SQL訪問MySql資料庫
  • EF Core + MySQL資料庫插入數據後獲取自增列的值

Entity Framework Core 簡稱為 EF Core

2、本教程環境信息

軟體/環境說明
操作系統 Windows 10
SDK 2.1.401
ASP.NET Core 2.1.3
MySQL 8.0.x
IDE Visual Studio Code 1.30
瀏覽器 Chrome 70
VS Code插件版本說明
C# 1.17.1 提供C#智能感知, .NET Core 調試、編譯等
vscdoe-solution-explorer 0.3.1 提供解決方案視圖

本篇代碼以下代碼進行調整:https://github.com/ken-io/asp.net-core-tutorial/tree/master/chapter-02

3、準備工作

安裝MySQL資料庫:https://dev.mysql.com/downloads/mysql/

  • Windows環境

下載安裝包後雙擊安裝,一直下一步即可。

  • MacOS環境

參考:https://ken.io/note/macos-mysql8-install-config-tutorial

  • CentOS環境

參考:https://ken.io/note/centos-mysql8-setup

4、前置知識

  • 控制反轉(IOC)原則與依賴註入(DI)

ASP.NET Core 預設集成了DI。所有官方模塊的引入都要使用DI的方式引入。

https://baike.baidu.com/item/IOC

  • Linq使用教程

https://docs.microsoft.com/zh-cn/dotnet/csharp/tutorials/working-with-linq

二、EF Core + MySQL 前置準備

EF Core 全稱:Entity Framework Core,為微軟為 .NET Core平臺開發的ORM框架。對應是 .NET Framework平臺的 Entity Framework(EF),無論是EF還是EF Core都可以說是 .NET 平臺開發效率最高的ORM框架。

1、引入 EF Core + MySQL Provider

EF Core已經集成在 ASP.NET Core 中,但預設並不支持MySQL,如果需要連接MySQL,需要添加MySQL相關的Provider,這裡我選擇的是:Pomelo.EntityFrameworkCore.MySql

任意命令行操作即可,我用的是VS Code自帶的命令行

//進入項目根目錄: Ken.Tutorial.Web
cd Ken.Tutorial.Web

//添加Package
dotnet add package Pomelo.EntityFrameworkCore.MySql

這裡我添加的 MySql.Data.EntityFrameworkCore 版本是 8.0.13,如果你想跟我使用一樣的版本,可以使用以下命令:

dotnet add package Pomelo.EntityFrameworkCore.MySql --version 2.1.4

2、創建MySQL庫表

  • 創建資料庫
CREATE DATABASE ken_tutorial;
  • 創建表
USE ken_tutorial;

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `hobby` varchar(500) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3、配置鏈接字元串

在項目根目錄Ken.Tutorial.Web中創建配置文件appsettings.json,並寫入以下配置

{
  "ConnectionStrings": {
    "testdb": "server=localhost;database=Ken.Tutorial;uid=root;pwd=root;"
  }
}

4、實體&DbContext準備

創建user表對應實體

在項目根目錄Ken.Tutorial.Web中創建目錄Models,併在其中創建類:UserEntity.cs

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Ken.Tutorial.Web.Models
{
    [Table("user")]
    [Serializable]
    public class UserEntity
    {
        [Key]//主鍵
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]//自增列
        [Column("id")]
        public int Id { get; set; }

        [Column("name")]
        public string Name { get; set; }

        [Column("age")]
        public int Age { get; set; }

        [Column("hobby")]
        public string Hobby { get; set; }
    }
}

創建DBContext

在項目根目錄Ken.Tutorial.Web中創建目錄Repositories,併在其中創建類:TutorialDbContext.cs

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace Ken.Tutorial.Web.Repositories
{
    public class TutorialDbContext : DbContext
    {
        private IConfiguration Configuration { get; }

        public TutorialDbContext(IConfiguration configuration)
        {
            this.Configuration = configuration;
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseMySql(Configuration.GetConnectionString("testdb"));
        }

        ppublic DbSet<UserEntity> Users { get; set; }
    }
}

TutorialDbContext 繼承了 DbContext,然後再Context初始化時配置MySQL鏈接。

由於 ASP.NET Core 預設使用了DI組件,所以我們取配置文件,就需要在構造函數中獲取 IConfiguration 註入的實例。

在Startup.cs配置註入,以便數據訪問類可以訪問DI方式獲取TutorialDbContext

public void ConfigureServices(IServiceCollection services)
{
    //引入MVC模塊
    services.AddMvc();

    //配置DbContext註入
    services.AddTransient<TutorialDbContext>();
}

三、EF Core + Linq to entity 訪問資料庫

1、創建Linq To Entity 數據訪問類

using System;
using System.Collections.Generic;
using System.Linq;
using Ken.Tutorial.Web.Models;

namespace Ken.Tutorial.Web.Repositories
{
    public class TutorialRepository
    {
        private TutorialDbContext DbContext { get; }

        public TutorialRepository(TutorialDbContext dbContext)
        {
            //在構造函數中註入DbContext
            this.DbContext = dbContext;
        }

        //添加
        public int Add(UserEntity user)
        {
            using (DbContext)
            {
                //由於我們在UserEntity.Id配置了自增列的Attribute,EF執行完成後會自動把自增列的值賦值給user.Id
                DbContext.Users.Add(user);
                return DbContext.SaveChanges();
            }

        }

        //刪除
        public int Delete(int id)
        {
            using (DbContext)
            {
                var userFromContext = DbContext.Users.FirstOrDefault(u => u.Id == id);
                DbContext.Users.Remove(userFromContext);
                return DbContext.SaveChanges();
            }
        }

        //更新
        public int Update(UserEntity user)
        {
            using (DbContext)
            {
                var userFromContext = DbContext.Users.FirstOrDefault(u => u.Id == user.Id);
                userFromContext.Name = user.Name;
                userFromContext.Age = user.Age;
                userFromContext.Hobby = user.Hobby;
                return DbContext.SaveChanges();
            }
        }

        //查詢
        public UserEntity QueryById(int id)
        {
            using (DbContext)
            {
                return DbContext.Users.FirstOrDefault(u => u.Id == id);
            }
        }

        //查詢集合
        public List<UserEntity> QueryByAge(int age)
        {
            using (DbContext)
            {
                return DbContext.Users.Where(u => u.Age == age).ToList();
            }
        }

        //查看指定列
        public List<string> QueryNameByAge(int age)
        {
            using (DbContext)
            {
                return DbContext.Users.Where(u => u.Age == age).Select(u => u.Name).ToList();
            }
        }

        //分頁查詢
        public List<UserEntity> QueryUserPaging(int pageSize, int page)
        {
            using (DbContext)
            {
                return DbContext.Users.Skip(pageSize * (page - 1)).Take(pageSize).ToList();
            }
        }

        //事務:將年齡<0的用戶修改年齡為0
        public int FixAge()
        {
            using (DbContext)
            {
                using (var transaction = DbContext.Database.BeginTransaction())
                {
                    try
                    {
                        var userListFromContext = DbContext.Users.Where(u => u.Age < 0);
                        foreach (UserEntity u in userListFromContext)
                        {
                            u.Age = 0;
                        }
                        var count = DbContext.SaveChanges();
                        transaction.Commit();
                        return count;
                    }
                    catch
                    {
                        transaction.Rollback();
                        return 0;
                    }
                }
            }
        }
    }
}

2、創建測試API

Controllers文件夾下創建EfCoreController.cs作為訪問入口

using System;
using Ken.Tutorial.Web.Models;
using Ken.Tutorial.Web.Repositories;
using Microsoft.AspNetCore.Mvc;

namespace Ken.Tutorial.Web.Controllers
{
    public class EfCoreController : Controller
    {
        public TutorialRepository Repository { get; }

        public EfCoreController(TutorialRepository repository)
        {
            this.Repository = repository;
        }

        public IActionResult Add(UserEntity user)
        {
            var message = Repository.Add(user) > 0 ? "success" : "failed";
            return Json(new { Message = message, User = user });
        }

        public IActionResult Delete(int id)
        {
            var message = Repository.Delete(id) > 0 ? "success" : "failed";
            return Json(new { Message = message });
        }

        public IActionResult Update(UserEntity user)
        {
            var message = Repository.Update(user) > 0 ? "success" : "failed";
            return Json(new { Message = message, User = user });
        }

        public IActionResult QueryById(int id)
        {
            var user = Repository.QueryById(id);
            return Json(new { User = user });
        }

        public IActionResult QueryByAge(int age)
        {
            var users = Repository.QueryByAge(age);
            return Json(new { Users = users });
        }

        public IActionResult QueryNameByAge(int age)
        {
            var users = Repository.QueryNameByAge(age);
            return Json(new { Users = users });
        }

        public IActionResult QueryUserPaging(int pageSize, int page)
        {
            var users = Repository.QueryUserPaging(pageSize, page);
            return Json(new { Users = users });
        }

        public IActionResult FixAge()
        {
            var count = Repository.FixAge();
            return Json(new { FixCount = count });
        }

    }
}

3、啟動測試

  • 在Startup.cs中配置Repository註入
public void ConfigureServices(IServiceCollection services)
{
    //其他代碼省略

    //配置Repository註入
    services.AddTransient<TutorialRepository>();
}
  • 啟動項目並測試
API示例
添加用戶 /efcore/add?name=ken&age=18&hobby=coding
刪除用戶 /efcore/delete?id=0
更新用戶 /efcore/update?id=1&name=ken&age=666&hobby=codingOrGaming
查詢單個用戶 /efcore/querybyid?id=0
查詢多個用戶 /efcore/querybyage?age=18
查詢多個用戶名 /efcore/querynamebyage?age=18
分頁查詢用戶 /efcore/queryuserpaging?pagesize=3&page=1
修複異常年齡 /efcore/fixage

四、EF Core + 原生SQL 訪問資料庫

1、創建EF Core + 原生SQL 數據訪問類

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using Ken.Tutorial.Web.Models;
using Microsoft.EntityFrameworkCore;
using MySql.Data.MySqlClient;

namespace Ken.Tutorial.Web.Repositories
{
    public class TutorialWithSqlRepository
    {
        private TutorialDbContext DbContext { get; }

        public TutorialWithSqlRepository(TutorialDbContext dbContext)
        {
            //在構造函數中註入DbContext
            this.DbContext = dbContext;
        }

        //添加
        public int Add(UserEntity user)
        {
            using (var connection = DbContext.Database.GetDbConnection())
            {
                connection.Open();
                var command = connection.CreateCommand() as MySqlCommand;
                command.CommandText = "INSERT INTO user (name,age,hobby) VALUES(@name,@age,@hobby)";
                command.Parameters.Add(new MySqlParameter()
                {
                    ParameterName = "@name",
                    DbType = DbType.String,
                    Value = user.Name
                });
                command.Parameters.Add(new MySqlParameter()
                {
                    ParameterName = "@age",
                    DbType = DbType.Int32,
                    Value = user.Age
                });
                command.Parameters.Add(new MySqlParameter()
                {
                    ParameterName = "@hobby",
                    DbType = DbType.String,
                    Value = user.Hobby
                });
                var count = command.ExecuteNonQuery();
                //獲取插入時產生的自增列Id並賦值給user.Id使用
                user.Id = (int)command.LastInsertedId;
                return count;
            }

        }

        //刪除
        public int Delete(int id)
        {
            using (DbContext)
            {
                return DbContext.Database.ExecuteSqlCommand(
                    "DELETE FROM user WHERE id={0}", id);
            }
        }

        //更新
        public int Update(UserEntity user)
        {
            using (DbContext)
            {
                return DbContext.Database.ExecuteSqlCommand(
                     "UPDATE user SET name={0}, age={1}, hobby={2} WHERE id={3}",
                      user.Name, user.Age, user.Hobby, user.Id);
            }
        }

        //查詢
        public UserEntity QueryById(int id)
        {
            using (DbContext)
            {
                return DbContext.Users.FromSql("SELECT id,name,age,hobby FROM user WHERE id={0}", id).FirstOrDefault();
            }
        }

        //查詢集合
        public List<UserEntity> QueryByAge(int age)
        {
            using (DbContext)
            {
                return DbContext.Users.FromSql("SELECT id,name,age,hobby FROM user WHERE age={0}", age).ToList();
            }
        }

        //查看指定列
        public List<string> QueryNameByAge(int age)
        {
            using (DbContext)
            {
                return DbContext.Users.FromSql("SELECT id,name FROM user WHERE age={0}", age).Select(u => u.Name).ToList();
            }
        }

        //分頁查詢
        public List<UserEntity> QueryUserPaging(int pageSize, int page)
        {
            using (DbContext)
            {
                return DbContext.Users.FromSql("SELECT id,name,age,hobby FROM user LIMIT {0},{1}", pageSize * (page - 1), pageSize).ToList();
            }
        }

        //事務:將年齡<0的用戶修改年齡為0
        public int FixAge()
        {
            using (DbContext)
            {
                using (var connection = DbContext.Database.GetDbConnection())
                {
                    //打開連接
                    connection.Open();
                    //開啟事務
                    using (var transaction = connection.BeginTransaction())
                    {
                        try
                        {
                            //獲取命令對象
                            var command = connection.CreateCommand();
                            command.Transaction = transaction;
                            command.CommandText = "UPDATE user SET age=@age WHERE age<@age";
                            command.Parameters.Add(new MySqlParameter()
                            {
                                ParameterName = "@age",
                                DbType = DbType.Int32,
                                Value = 0
                            });
                            var count = command.ExecuteNonQuery();
                            transaction.Commit();
                            return count;
                        }
                        catch (Exception ex)
                        {
                            connection.Close();
                            transaction.Rollback();
                            return 0;
                        }
                    }

                }
            }
        }
    }
}

2、創建測試API

Controllers文件夾下創建EfCoreWithSqlController.cs作為訪問入口

using System;
using Ken.Tutorial.Web.Models;
using Ken.Tutorial.Web.Repositories;
using Microsoft.AspNetCore.Mvc;

namespace Ken.Tutorial.Web.Controllers
{
    public class EfCoreWithSqlController : Controller
    {
        public TutorialWithSqlRepository Repository { get; }

        public EfCoreWithSqlController(TutorialWithSqlRepository repository)
        {
            this.Repository = repository;
        }

        public IActionResult Add(UserEntity user)
        {
            var message = Repository.Add(user) > 0 ? "success" : "failed";
            return Json(new { Message = message, User = user });
        }

        public IActionResult Delete(int id)
        {
            var message = Repository.Delete(id) > 0 ? "success" : "failed";
            return Json(new { Message = message });
        }

        public IActionResult Update(UserEntity user)
        {
            var message = Repository.Update(user) > 0 ? "success" : "failed";
            return Json(new { Message = message, User = user });
        }

        public IActionResult QueryById(int id)
        {
            var user = Repository.QueryById(id);
            return Json(new { User = user });
        }

        public IActionResult QueryByAge(int age)
        {
            var users = Repository.QueryByAge(age);
            return Json(new { Users = users });
        }

        public IActionResult QueryNameByAge(int age)
        {
            var users = Repository.QueryNameByAge(age);
            return Json(new { Users = users });
        }

        public IActionResult QueryUserPaging(int pageSize, int page)
        {
            var users = Repository.QueryUserPaging(pageSize, page);
            return Json(new { Users = users });
        }

        public IActionResult FixAge()
        {
            var count = Repository.FixAge();
            return Json(new { FixCount = count });
        }

    }
}

3、啟動測試

  • 在Startup.cs中配置Repository註入
public void ConfigureServices(IServiceCollection services)
{
    //其他代碼省略

    //配置Repository註入
    services.AddTransient<TutorialWithSqlRepository>();
}
  • 啟動項目並測試
API示例
添加用戶 /efcorewithsql/add?name=ken&age=18&hobby=coding
刪除用戶 /efcorewithsql/delete?id=0
更新用戶 /efcorewithsql/update?id=1&name=ken&age=666&hobby=codingOrGaming
查詢單個用戶 /efcorewithsql/querybyid?id=0
查詢多個用戶 /efcorewithsql/querybyage?age=18
查詢多個用戶名 /efcorewithsql/querynamebyage?age=18
分頁查詢用戶 /efcorewithsql/queryuserpaging?pagesize=3&page=1
修複異常年齡 /efcorewithsql/fixage

五、備註

1、附錄

  • 本文代碼示例

https://github.com/ken-io/asp.net-core-tutorial/tree/master/chapter-08

  • 本文參考

https://docs.microsoft.com/zh-cn/ef/core
https://www.learnentityframeworkcore.com
https://mysql-net.github.io/MySqlConnector/tutorials/net-core-mvc/



本文首發於我的獨立博客:https://ken.io/note/asp.net-core-tutorial-entity-framework-core-mysql


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 使用DB查詢,必須use Illuminate\Support\Facades\DB; 多數組條件查詢單條數據 first() 解釋下哈,這裡是前臺模板提交ajax提交來的post方式數據,這裡我沒有用到Model層,也就是Model層邏輯在Controller里寫了,這也是可以的哈 查詢多條數據用 ...
  • 1.個人思路 1.情景假設 一天,班主任佈置了一個任務:給所有的小朋友都發了一個牌子,每個牌子上都有一個數字,誰找到兩個牌子合起來的數字等於老師的牌子,那麼可以贏的一個蘋果。 這個時候小朋友,如果你是小朋友。肯定先看自己牌子的數字,然後在看看老師牌子的數字,找數學老師算出需要的數字,然後去看看誰的牌 ...
  • 上兩節介紹完Hybrid模式在MVC下的使用,包括驗證從數據獲取的User和Claim對MVC的身份授權。本節將介紹Implicit模式在JavaScript應用程式中的使用,使用Node.js+Express構建JavaScript客戶端,實現前後端分離。本節授權服務和資源伺服器基於第四和第五節。 ...
  • 在上一篇博文《 "[UWP]在UWP平臺中使用Lottie動畫" 》中我簡單介紹了一下LottieUWP項目以及如何使用它呈現Lottie動畫,這篇文章里我們來講點進階的東西——緩存Lottie動畫幀。 為什麼會有這樣的需求呢? 有兩方面原因: 直接在XAML中使用Lottie動畫時,是邊播放邊渲染 ...
  • 最近QQ影音久違的更新了,因為記得QQ影音之前體驗還算不錯(FFmepg的事另說),我也第一時間去官網下載體驗了一下,結果發現一些有趣的事情。 是的,你沒看錯,QQ影音主界面上這個動畫效果是使用Lottie動畫實現的! 這讓我大為驚奇,我對Lottie瞭解還算是比較多的,但是Lottie常見應用於移 ...
  • " 【.NET Core項目實戰 統一認證平臺】開篇及目錄索引 " 上篇文章介紹了基於 密碼授權模式,從使用場景、原理分析、自定義帳戶體系集成完整的介紹了密碼授權模式的內容,並最後給出了三個思考問題,本篇就針對第一個思考問題詳細的講解下 是如何生成access_token的,如何驗證access_t ...
  • 很久沒有寫博客了,最近做了一個公司門戶網站的小項目,其中接觸到了一些我不會的知識點,今日事情少,便記錄一下,當時想在網上搜索相關的內容,但是沒有找到。 今天想記錄一下這樣一個小的需求的做法。先說一下我的想法:因為是一個門戶網站,所以我需要從後臺傳大量的數據到前臺,我考慮的是這樣做,用一個字典類型(d ...
  • 二分搜索用於在已經排序好的集合中搜索值,每次與中間值對比,小於則搜索前半段,大於中間值則在後半段,繼續二分搜索,實現代碼: 如果查詢不到值返回的是負的最後查詢的中間值的位置,負值變正後+1 則可用來有序插入搜索值,使列表保持排序。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...