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

ADO.NET Entity Framework 面试题

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (106投票s)

2013年10月30日

CPOL

13分钟阅读

viewsIcon

623245

downloadIcon

17105

快速回顾 25 个重要的 ADO.NET Entity Framework 面试题及答案。

目录

什么是 Entity Framework?

ADO.NET entity 是一个 ORM(对象关系映射),它在 ADO.NET 组件之上创建了一个更高级别的抽象对象模型。因此,您不必像下面的代码所示那样深入到 dataset、datatables、command 和 connection 对象,而是可以处理更高级别的域对象,如客户、供应商等。

DataTable table = adoDs.Tables[0];
for (int j = 0; j < table.Rows.Count; j++)
{
    DataRow row = table.Rows[j];

    // Get the values of the fields
    string CustomerName =
        (string)row["Customername"];
    string CustomerCode =
        (string)row["CustomerCode"];
}

下面是 Entity Framework 的代码,其中我们处理的是更高级别的域对象,如客户,而不是像 dataset、datareader、command、connection 对象等基线 ADO.NET 组件。

foreach (Customer objCust in obj.Customers)
{}

使用 EF 有哪些好处?

EF 的主要也是唯一的好处是它能自动生成模型(中间层)、数据访问层和映射代码,从而大大减少了开发时间。

创建这些领域/实体对象有哪些不同的方法?

实体对象可以通过两种方式创建:从数据库结构创建,或从头开始创建模型。

Entity Framework 对话框中的复数化和单数化是什么意思?

“复数化”和“单数化”为对象提供有意义的命名约定。简单来说,它询问您是否希望使用以下命名约定来表示您的对象:

  • 一个客户记录意味着“Customer”(单数)。
  • 大量客户记录意味着“Customer’s”(复数,注意“s”)

如果选择下面的复选框,Entity Framework 会生成一个符合复数和单数编码约定的命名约定。

EDMX 文件在 Entity Framework 中有什么重要性?

EDMX(Entity Data Model XML)是一个 XML 文件,其中包含有关对象如何映射到 SQL 表的所有映射详细信息。EDMX 文件进一步分为三个部分:CSDL、SSDL 和 MSL。

你能解释一下 EDMX 文件中的 CSDL、SSDL 和 MSL 部分吗?

  • CSDL(概念模式定义语言)是暴露给应用程序的概念抽象。
  • SSDL(存储模式定义语言)定义了与您的 RDBMS 数据结构的映射。
  • MSL(映射模式语言)连接 CSDL 和 SSDL。

CSDL、SSDL 和 MSL 实际上是 XML 文件。

图:CSDL、MSL 和 SSDL

什么是 T4 模板?

T4(Text Template Transformation Toolkit)是一个基于模板的代码生成引擎。您可以在 T4 模板(*.tt 是扩展名)文件中编写 C# 代码,然后这些 C# 代码会执行以根据编写的 C# 逻辑生成文件。

例如,下面的 T4 C# 代码

<#@ template language=“C#” #>
Hello <# Write(”World!”) #> 

将生成以下 C# 输出

Hello
World !

T4 在 Entity Framework 中有什么重要性?

T4 文件是 EF 代码生成的灵魂。T4 代码模板读取 EDMX XML 文件并生成 C# 后台代码。此 C# 后台代码就是您的实体和上下文类。

如果您使用 VS 2012 创建项目,您将看到以下层次结构。顶部是 EDMX 文件,接着是 TT 或 T4 文件,然后是 .CS 代码文件。

如何使用 Entity Framework 类读取记录?

为了浏览记录,您可以创建上下文类的对象,并且在该上下文类中,您将获得记录。

例如,在下面的代码片段中,我们正在循环遍历一个 Customer 对象集合。这个 Customer 集合是上下文类CustomermytextEntities的输出。

CustomermytestEntities obj = new CustomermytestEntities();
foreach (Customer objCust in obj.Customers)
{}

如何使用 EF 添加、更新和删除?

创建实体类的对象,使用AddObject方法将其添加到数据上下文中,然后调用SaveChanges方法。

CustomermytestEntities obj = new CustomermytestEntities();
Customer objCust = new Customer();
objCust.CustomerCode = "1001";
obj.Customers.AddObject(objCust);
obj.SaveChanges();

如果您想更新,请选择对象,修改对象,然后调用AcceptAllChanges

CustomermytestEntities objContext = new CustomermytestEntities();
Customer objCustomer = (Customer)objContext.Customers.FirstOrDefault();
objCustomer.CountryCode = "NEP";
objContext.AcceptAllChanges();

如果您想删除,请调用DeleteObject方法,如以下代码片段所示。

CustomermytestEntities objContext = new CustomermytestEntities();
Customer objCustomer = (Customer)objContext.Customers.FirstOrDefault();
objContext.DeleteObject(objCustomer);

您可以观看下面的 YouTube 视频,其中展示了使用 Entity Framework 进行简单的插入、更新和删除示例。

人们说 Entity Framework 运行缓慢

默认情况下,EF 具有延迟加载行为。由于这种默认行为,如果您加载大量记录,特别是当它们具有外键关系时,可能会出现性能问题。因此,当您确实需要所有场景下的延迟加载行为时,需要谨慎。为了获得更好的性能,在加载大量记录时禁用延迟加载或使用存储过程。

你能详细解释一下延迟加载吗?

延迟加载是一种概念,我们按需加载对象,而不是一次性加载所有对象。考虑一种情况,Customer 和 Address 对象之间存在一对多关系。现在,假设您正在浏览客户数据,但当时不想加载地址数据。但是,当您开始访问 Address 对象时,您希望从数据库加载地址数据。

Entity Framework 默认启用了延迟加载行为。例如,考虑以下代码。当我们对 Customer 对象进行foreach循环时,Address 对象不会被加载。但是,当您开始对 Address 集合进行foreach循环时,Address 对象会通过执行 SQL 查询从 SQL Server 加载。

因此,简单来说,它将为客户的每个 Address 记录执行一个单独的查询,这对于大量记录来说肯定是不好的。

MyEntities context = new MyEntities();

var Customers = context.Customers.ToList();

foreach (Customercust in Customers) // In this line no address object loaded
{
     foreach(Address add in cust.Addresses){}// Address object is loaded here
}

如何关闭延迟加载?

延迟加载的反义词是急切加载。在急切加载中,我们提前加载对象。因此,第一件事是我们需要通过将LazyLoadingEnabled设置为false来禁用延迟加载。

context.ContextOptions.LazyLoadingEnabled = false;

现在,我们必须使用include函数明确告诉 EF 我们想要加载哪些对象。下面是一个简单的示例代码,其中我们使用include函数告诉 EF 同时加载 Customer 和 Address 对象。

现在,Customer 对象和相关的 Address 对象将在一个查询中加载,而不是多个查询。

var employees = context.Customers.Include("Addresses").Take(5);

如何在 Entity Framework 中使用存储过程?

您可以在 EDMX 中使用存储过程映射详细信息,如以下图所示。

图:指定存储过程

Entity Framework 中的 POCO 类是什么?

POCO 意为 Plain Old C# Object(纯粹的 C# 对象)。当 EDMX 创建类时,它们会充斥着大量的实体标签。例如,下面是一个使用 Entity Framework 生成的简单 Customer 类。很多时候,我们希望使用简单的 .NET 类并将其与 Entity Framework 集成。

Entity Framework 支持这一点。换句话说,您可以创建一个简单的 .NET 类,并使用实体上下文对象来加载您的简单 .NET 类。

下面是一个 EF 生成的简单类,它充斥着大量的 EF 属性。

[EdmEntityTypeAttribute(NamespaceName="CustomermytestModel", Name="Customer")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class Customer : EntityObject
{
    #region Factory Method

    /// <summary>
    /// Create a new Customer object.
    /// </summary>
    /// <param name="id" />Initial value of the Id property.
    /// <param name="customerCode" />Initial value of the CustomerCode property.
    /// <param name="customername" />Initial value of the Customername property.
    public static Customer CreateCustomer(global::System.Int32 id, 
       global::System.String customerCode, global::System.String customername)
    {
        Customer customer = new Customer();
        customer.Id = id;
        customer.CustomerCode = customerCode;
        customer.Customername = customername;
        return customer;
    }

    #endregion
    #region Primitive Properties

如何在 Entity Framework 中实现 POCO?

实现 POCO 是一个三步过程:

public class Customer
{
    private string _customerName;

    public string CustomerName
    {
        get { return _customerName; }
        set { _customerName = value; }
    }

    private int _Customerid;

    public int Customerid
    {
        get { return _Customerid; }
        set { _Customerid = value; }
    }

}
public partial class Test123Entities : ObjectContext
{ 
    public Test123Entities()
        : base("name=Test123Entities", "Test123Entities")
    {
        this.ContextOptions.LazyLoadingEnabled = true;
        OnContextCreated();
    }
    partial void OnContextCreated();
    public ObjectSet<Customer> Customers
    {
        get
        {
            if ((_Customers == null))
            {
                _Customers = base.CreateObjectSet<Customer>("Customers");
            }
            return _Customers;
        }
    }
    private ObjectSet<Customer> _Customers;
    public void AddToCustomers(Customer customer)
    {
        base.AddObject("Customers", customer);
    }
} 
  • 转到设计器并将代码生成策略设置为NONE。此步骤意味着您将自己生成类,而不是依赖 EF 的自动代码生成。
  • 现在我们已经停止了自动代码生成,我们需要手动创建域类。添加一个类文件并创建域类,就像我们之前创建的Customer类一样。
  • 编写继承自ObjectContext的上下文层代码。您可以从 EF 的后台代码中复制代码并粘贴,也可以在禁用自动生成之前完成。

最后,您可以在客户端像往常一样使用 EF 来使用上述代码。

Test123Entities oContext = new Test123Entities();
List<Customer> oCustomers = oContext.Customers.ToList<Customer>(); 

在 POCO 类中,我们需要 EDMX 文件吗?

是的,您仍然需要 EDMX 文件,因为上下文对象读取 EDMX 文件来进行映射。

Entity Framework 中的 Code First 方法是什么?

在 Code First 方法中,我们避免使用 Entity Framework 的 Visual Designer。换句话说,EDMX 文件被排除在解决方案之外。因此,您现在可以完全控制上下文类和实体类。

POCO、Code First 和简单的 EF 方法之间有什么区别?

这三种方法都定义了您对 Entity Framework 代码的控制程度。Entity Framework 是一个 ORM,它生成大量代码,创建您的中间层(Entity)和数据访问层(Context)。

但是很多时候,您希望享受两全其美的好处,您希望通过自动生成部分来最小化开发时间,同时又希望控制代码以便于维护代码质量。

下面是定义每种方法的区别表。在简单的 Entity Framework 中,一切都是自动生成的,因此您还需要 EDMX XML 文件。POCO 是半自动的,所以您对实体类有完全的控制,但上下文类仍然由 EDMX 文件生成。

在 Code First 中,您可以完全控制如何创建实体类和上下文类。因为您将手动创建这些类,所以您不依赖于 EDMX XML 文件。下面是一个简单的表格,显示了交叉比较。

  EDMX 实体 背景
简单的 Entity Framework 需要 自动 自动
POCO 方法 需要 手动 自动
Code First 不需要 手动 手动

如何在 Entity Framework 中处理并发?

注意:在此问题之前,面试官可能会询问您关于并发以及什么是悲观锁定和乐观锁定。请参阅 ADO.NET 章节以了解这些内容。

在 EF 中,通过使用乐观锁定来解决并发问题。请参阅 ADO.NET 章节了解什么是乐观锁定和悲观锁定?要实现乐观锁定,请右键单击 EDMX 设计器并将并发模式设置为Fixed,如以下图所示。

现在,每当出现并发问题时,您应该会收到一个OptimisticConcurrencyException错误,如以下图所示。然后,您可以放置一个try / catch块来处理这种情况。

如何在 Entity Framework 中进行悲观锁定?

我们无法使用 Entity Framework 进行悲观锁定。您可以从 Entity Framework 调用存储过程,并通过在存储过程中设置隔离级别来进行悲观锁定。但 Entity Framework 本身不支持悲观锁定。

Entity Framework 并发中的客户端获胜和存储端获胜模式是什么?

客户端获胜(Client wins)和存储端获胜(Store wins)是在发生并发时您希望采取的操作。在存储端获胜/数据库获胜模式下,服务器上的数据会被加载到您的实体对象中。客户端获胜模式与存储端获胜模式相反,实体对象中的数据会被保存到数据库中。

我们需要使用 Entity Framework 上下文的Refresh方法,并提供RefreshMode枚举值。下面是一个执行ClientWins的简单代码片段。

Context.Refresh(System.Data.Objects.RefreshMode.ClientWins,Obj);

Entity Framework 中的标量属性和导航属性是什么?

标量属性是包含实体实际值的属性。例如,在上面的 Customer 实体中,customernamecustomerid是标量属性。通常,标量属性会映射到一个数据库字段。

导航属性有助于从一个实体导航到另一个实体。例如,考虑以下示例,其中我们有两个实体:Customer 和 Address,一个 Customer 有多个 Address 对象。

现在,我们希望有一种机制,在任何给定时刻,我们都可以从一个给定的 Customer 对象导航到 Addresses 集合,并从 Address 对象导航到 Customer。

如果您打开实体设计器,您会注意到导航属性,如下图所示。导航属性是从主键和外键引用自动创建的。

因此,现在由于这些导航属性,我们可以从Customer导航到Addresses对象,请看下面的代码。

Customer Cust =  oContext.Customers.ToList<Customer>()[0];
// From customer are browsing addresses
List<Address> Addresses = Cust.Addresses.ToList<Address>(); 

您也可以反过来操作。换句话说,从Address对象,您可以引用Customer对象,如以下代码所示。

Address myAddress = Addresses[0];

// From address we can browse customer
Customer cus = myAddress.Customer;

Entity Framework 中的复杂类型是什么?

有时,不同的实体可能具有共同的属性。例如,考虑以下图,其中我们有CustomerSupplier实体。它们有三个共同的字段:Address1Address2PhoneNo。这些字段同时出现在CustomerSupplier实体中。

因此,为了消除这些重复和冗余的字段,我们可以将它们移到一个名为Address的通用复杂类型中。复杂类型将公共字段分组,以便它们可以在实体之间重用。

要创建复杂类型,请选择要分组到复杂类型的字段,单击“重构”(Refactor),然后创建复杂类型。下图显示了这一点。创建复杂类型后,您还可以将该复杂类型与其他实体一起重用。

LINQ to SQL 和 Entity Framework 之间有什么区别?

  • LINQ to SQL 适用于与 SQL Server 进行快速开发。EF 适用于企业场景,并且可以与 SQL Server 以及其他数据库配合使用。
  • LINQ 直接映射到表。一个 LINQ 实体类映射到一个表。EF 具有概念模型,该概念模型通过映射映射到存储模型。因此,一个 EF 类可以映射到多个表,或者一个表可以映射到多个类。
  • LINQ 更侧重于快速开发,而 EF 则用于企业级别,其需求是开发一个松耦合的框架。

DbContext 和 ObjectContext 之间有什么区别?

DbContextObjectContext的包装器,它是ObjectContext的简化版本。

作为开发人员,您可以从DbContext开始,因为它易于使用。当您觉得某些操作DbContext无法实现时,您可以从 DbContext 访问ObjectContext,如以下代码所示。

((IObjectContextAdapter)dbContext).ObjectContext

注意:如果面试官询问DbContext不支持哪些操作,您可以借口说您记不清了。想知道为什么有时面试官会问 API 级别的​​问题吗?

如需进一步阅读,请观看以下面试准备视频和分步视频系列。

© . All rights reserved.