ADO.NET Entity Framework 面试题






4.90/5 (106投票s)
快速回顾 25 个重要的 ADO.NET Entity Framework 面试题及答案。
目录
- 什么是 Entity Framework?
- 使用 EF 有哪些好处?
- 创建这些领域/实体对象有哪些不同的方法?
- Entity Framework 对话框中的复数化和单数化是什么意思?
- EDMX 文件在 Entity Framework 中有什么重要性?
- 你能解释一下 EDMX 文件中的 CSDL、SSDL 和 MSL 部分吗?
- 什么是 T4 模板?
- T4 在 Entity Framework 中有什么重要性?
- 如何使用 Entity Framework 类读取记录?
- 如何使用 EF 添加、更新和删除?
- 人们说 Entity Framework 运行缓慢
- 你能详细解释一下延迟加载吗?
- 如何关闭延迟加载?
- 如何在 Entity Framework 中使用存储过程?
- Entity Framework 中的 POCO 类是什么?
- 如何在 Entity Framework 中实现 POCO?
- 在 POCO 类中,我们需要 EDMX 文件吗?
- Entity Framework 中的 Code First 方法是什么?
- POCO、Code First 和简单的 EF 方法之间有什么区别?
- 如何在 Entity Framework 中处理并发?
- 如何在 Entity Framework 中进行悲观锁定?
- Entity Framework 并发中的客户端获胜和存储端获胜模式是什么?
- Entity Framework 中的标量属性和导航属性是什么?
- Entity Framework 中的复杂类型是什么?
- LINQ to SQL 和 Entity Framework 之间有什么区别?
- DbContext 和 ObjectContext 之间有什么区别?
什么是 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 文件。
什么是 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 实体中,customername
和customerid
是标量属性。通常,标量属性会映射到一个数据库字段。
导航属性有助于从一个实体导航到另一个实体。例如,考虑以下示例,其中我们有两个实体: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 中的复杂类型是什么?
有时,不同的实体可能具有共同的属性。例如,考虑以下图,其中我们有Customer
和Supplier
实体。它们有三个共同的字段:Address1
、Address2
和PhoneNo
。这些字段同时出现在Customer
和Supplier
实体中。
因此,为了消除这些重复和冗余的字段,我们可以将它们移到一个名为Address
的通用复杂类型中。复杂类型将公共字段分组,以便它们可以在实体之间重用。
要创建复杂类型,请选择要分组到复杂类型的字段,单击“重构”(Refactor),然后创建复杂类型。下图显示了这一点。创建复杂类型后,您还可以将该复杂类型与其他实体一起重用。
LINQ to SQL 和 Entity Framework 之间有什么区别?
- LINQ to SQL 适用于与 SQL Server 进行快速开发。EF 适用于企业场景,并且可以与 SQL Server 以及其他数据库配合使用。
- LINQ 直接映射到表。一个 LINQ 实体类映射到一个表。EF 具有概念模型,该概念模型通过映射映射到存储模型。因此,一个 EF 类可以映射到多个表,或者一个表可以映射到多个类。
- LINQ 更侧重于快速开发,而 EF 则用于企业级别,其需求是开发一个松耦合的框架。
DbContext 和 ObjectContext 之间有什么区别?
DbContext
是ObjectContext
的包装器,它是ObjectContext
的简化版本。
作为开发人员,您可以从DbContext
开始,因为它易于使用。当您觉得某些操作DbContext
无法实现时,您可以从 DbCo
ntext 访问ObjectContext
,如以下代码所示。
((IObjectContextAdapter)dbContext).ObjectContext
注意:如果面试官询问DbContext
不支持哪些操作,您可以借口说您记不清了。想知道为什么有时面试官会问 API 级别的问题吗?
如需进一步阅读,请观看以下面试准备视频和分步视频系列。