0
var newcontact = context.Contact.CreateContact(0, "Julio", "Bartowski", DateTime.Now, DateTime.Now, null);

var nAddr = Address.CreateAddress(0, "Home", 0, DateTime.Now, null);
nAddr.Contact = newcontact;
nAddr.Dump();

var newaddress = new Address(){
    Street1 = "5907 Hollow Oak Ct",
    City = "Burke",
    StateProvince = "VA",
    CountryRegion = "US",
    PostalCode = "22015",
    AddressType = "Home",
    ModifiedDate = DateTime.Now
};
newaddress.Contact = newcontact;

this.AddToContacts(newcontact);  //I get an error here when new Addresses are waiting for the ContactID value.
this.SaveChanges();
newcontact.Dump();

我的目标是使用带有 SQL Server 后端的 Visual Studio 11 Beta(2012 年 5 月 26 日)中的最新 NuGet 将 Access 作为后端使用 Access 作为后端升级到带有实体框架 (EF) 的 MVC3 Razor 页面的旧的 1990 年代经典 ASP 应用程序。最终,我们会将应用程序从客户端服务器移到云中并移至 Azure。

从零知识开始并关注对象关系阻抗不匹配,我尝试在控制台应用程序和 LinqPad 中使用 EF 示例的基本介绍(一种非常快速、方便、简单的方法来开发和测试查询:-) Windows 7 笔记本电脑。

来自 Julia Lerman 的第一版编程实体框架的第 130 页的 BreakAway Geek Adventure (BAGA) 演示似乎是一个不错的起点。我对自我跟踪实体和附加和分离、AutoMapper 和实体框架的其他方面了解不多,但我知道基本介绍不应该让我想把使用微软 EF 的想法冲到马桶上去Ruby-on-Rails ActiveRecord 和 Amazon Web Services (AWS) 上的 LAMP 堆栈。

下面的 C# EF 示例片段来自一位公认的专家,应该非常简单......不幸的是,Stack Overflow 中包含“可能”和“可能”的大量答案表明 EF 通常没有被很好地理解。

例如,ADO.NET Entity Framework 和标识列以及对 MSDN 的引用都没有帮助。

虽然 MSDN 说 EF 在http://blogs.msdn.com/b/dsimmons/archive/2008/08/10/ef-faq-entity-services.aspx#Section_17上支持服务器生成的键值, 但我不相信他们,因为当我向实体集/表添加行/记录/实体时,EF 中不会返回联系人的身份,导致后续插入取决于 ContactID 失败。

本书的勘误网站没有给出很多关于调试的提示。在 Lerman 女士的第 1 版和第 2 版之间,基本的 EF 基础知识会发生很大变化吗?当我告诉她我的新小狗开始吃我新买的第一版时,她确实建议我把它还给他,给她买新书。

有没有其他人有这个问题?由于我是乘坐短途巴士长大的,因此问题可能出在椅子和键盘之间。希望这里有足够的信息来复制问题或提供一些调试提示。

Contact 表在数据库中定义了一个自动递增的主键。这是 T-SQL:

CREATE TABLE [dbo].[Contact](
    [ContactID] [int] IDENTITY(1,1) NOT NULL,
    [FirstName] [nvarchar](50) NOT NULL,
    [LastName] [nvarchar](50) NOT NULL,
    [Title] [nvarchar](50) NULL,
    [AddDate] [datetime] NOT NULL,
    [ModifiedDate] [datetime] NOT NULL,
    [RowVersion] [timestamp] NOT NULL,
 CONSTRAINT [PK_Contact] PRIMARY KEY CLUSTERED 
(
    [ContactID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

Address 表的 ContactID 与上面的 Contact 表有外键关系。

CREATE TABLE [dbo].[Address](
    [addressID] [int] IDENTITY(1,1) NOT NULL,
    [Street1] [nvarchar](50) NULL,
    [Street2] [nvarchar](50) NULL,
    [City] [nvarchar](50) NULL,
    [StateProvince] [nvarchar](50) NULL,
    [CountryRegion] [nvarchar](50) NULL,
    [PostalCode] [nvarchar](50) NULL,
    [AddressType] [nvarchar](50) NOT NULL,
    **[ContactID] [int] NOT NULL**,
    [ModifiedDate] [datetime] NOT NULL,
    [RowVersion] [timestamp] NOT NULL,
 CONSTRAINT [PK_Address] PRIMARY KEY CLUSTERED 
(
    [addressID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[Address]  WITH CHECK ADD  CONSTRAINT [FK_Address_Contact] FOREIGN KEY([ContactID])
REFERENCES [dbo].[Contact] ([ContactID])
GO

ALTER TABLE [dbo].[Address] CHECK CONSTRAINT [FK_Address_Contact]
GO

测试表及其关系:

DECLARE @ContactID int

INSERT INTO [dbo].[Contact]
           ([FirstName]
           ,[LastName]
           ,[Title]
           ,[AddDate]
           ,[ModifiedDate])
     VALUES
           ('Julio', 'Bartowski', 'Mr.', GETDATE(), GETDATE() )

SET @ContactID = SCOPE_IDENTITY()

PRINT @ContactID

-- this works
INSERT INTO [dbo].[Address]
           ([Street1]
           ,[Street2]
           ,[City]
           ,[StateProvince]
           ,[CountryRegion]
           ,[PostalCode]
           ,[AddressType]
           ,[ContactID]
           ,[ModifiedDate])
     VALUES
           ( '5907 Hollow Oak Ct', 'Suite 1400', 'Burke', 'VA', 'US', '22015', 'Office', @ContactID, GETDATE())

--this raises a referential integrity error (as it should) because there is no Contact with ContactID = 99999
INSERT INTO [dbo].[Address]
           ([Street1]
           ,[Street2]
           ,[City]
           ,[StateProvince]
           ,[CountryRegion]
           ,[PostalCode]
           ,[AddressType]
           ,[ContactID]
           ,[ModifiedDate])
     VALUES
           ( 'BadRef Way', '13th Floor', 'Manhattan', 'NY', 'US', '10004', 'Office', 99999, GETDATE())
GO

INSERT 语句与 FOREIGN KEY 约束“FK_Address_Contact”冲突。冲突发生在数据库“BreakAway”、表“dbo.Contact”、列“ContactID”中。该语句已终止。

    --show the correct inserts worked fine in T-SQL.
SELECT * FROM Contact c 
    LEFT OUTER JOIN Address a 
    ON c.ContactID = a.ContactID
    WHERE c.FirstName LIKE '%Julio%'

我假设在Code Through the Pain中描述的 SSDL 错误已经修复。以防万一,从我的实体框架 ADO.NET 实体数据模型 BAModel.EDMX 文件中提取的内容显示 ContactID 是存储架构定义语言和概念架构定义语言中的自动递增密钥标识:

  <edmx:Runtime>
    <!-- SSDL content -->
    <edmx:StorageModels>
    <Schema Namespace="BreakAwayModel.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2008" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">
        <EntityContainer Name="BreakAwayModelStoreContainer">
<EntityType Name="Address">
  <Key>
    <PropertyRef Name="addressID" />
  </Key>
  <Property Name="addressID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
  <Property Name="Street1" Type="nvarchar" MaxLength="50" />
  <Property Name="Street2" Type="nvarchar" MaxLength="50" />
  <Property Name="City" Type="nvarchar" MaxLength="50" />
  <Property Name="StateProvince" Type="nvarchar" MaxLength="50" />
  <Property Name="CountryRegion" Type="nvarchar" MaxLength="50" />
  <Property Name="PostalCode" Type="nvarchar" MaxLength="50" />
  <Property Name="AddressType" Type="nvarchar" Nullable="false" MaxLength="50" />
  <Property Name="ContactID" Type="int" Nullable="false" />
  <Property Name="ModifiedDate" Type="datetime" Nullable="false" />
  <Property Name="RowVersion" Type="timestamp" Nullable="false" StoreGeneratedPattern="Computed" />
</EntityType>
<EntityType Name="Contact">
  <Key>
    <PropertyRef Name="ContactID" />
  </Key>
  <Property Name="ContactID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
  <Property Name="FirstName" Type="nvarchar" Nullable="false" MaxLength="50" />
  <Property Name="LastName" Type="nvarchar" Nullable="false" MaxLength="50" />
  <Property Name="Title" Type="nvarchar" MaxLength="50" />
  <Property Name="AddDate" Type="datetime" Nullable="false" />
  <Property Name="ModifiedDate" Type="datetime" Nullable="false" />
  <Property Name="RowVersion" Type="timestamp" Nullable="false" StoreGeneratedPattern="Computed" />
</EntityType>

<!-- CSDL content -->
<edmx:ConceptualModels>
  <Schema Namespace="BAModel" Alias="Self" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
    <EntityContainer Name="BAEntities" annotation:LazyLoadingEnabled="true">

    <EntityType Name="Contact">
      <Key>
        <PropertyRef Name="ContactID" />
      </Key>
      <Property Name="ContactID" Type="Int32" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
      <Property Name="FirstName" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" />
      <Property Name="LastName" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" />
      <Property Name="Title" Type="String" MaxLength="50" Unicode="true" FixedLength="false" />
      <Property Name="AddDate" Type="DateTime" Nullable="false" Precision="3" />
      <Property Name="ModifiedDate" Type="DateTime" Nullable="false" Precision="3" />
      <Property Name="RowVersion" Type="Binary" Nullable="false" MaxLength="8" FixedLength="true" annotation:StoreGeneratedPattern="Computed" />
      <NavigationProperty Name="Addresses" Relationship="BAModel.FK_Address_Contact" FromRole="Contact" ToRole="Address" />
      <NavigationProperty Name="Customer" Relationship="BAModel.FK_Customers_Contact" FromRole="Contact" ToRole="Customers" />
      <NavigationProperty Name="Lodgings" Relationship="BAModel.FK_Lodging_Contact" FromRole="Contact" ToRole="Lodging" />
      <NavigationProperty Name="Payments" Relationship="BAModel.FK_Payment_Contact" FromRole="Contact" ToRole="Payment" />
      </EntityType>

下面是在 LinqPad 中输入的 C# 语句,它会自动连接到 BAGA.dll 程序集,而不必担心metadata=res://*/BAModel.csdl|res://*/BAModel.ssdl|res://*/BAModel.msl;.

事实上,我可以使用 lambda 查询 Contact 实体,这意味着我正确编译了 EDMX,而且我不是一个彻头彻尾的白痴——至少我妈妈是这么说的。

注意:“this”是 LinqPad 引用实体集合的方式,您的查询在该集合中从 DLL 程序集运行,而 .Dump() 是 LinqPad 用于显示输出的方法。在 LinqPad 中不需要“this”,但设置“context”变量让我可以使用与我的 ASP.NET Visual Studio C# 代码相同的语句。

var context = this;

context.Contacts
    .Include("Addresses")
    .OrderByDescending(x => x.ContactID)
    .Take(3)
    //.Single()  //only works if you Take(1)
    .Dump();

我还在控制台应用程序中尝试了这个演示,结果相似......

实体框架不检索新的 ContactID 身份。在 SaveChanges() 之后,ContactID 保持为 0,随后发生依赖于 ContactID 的地址插入失败。

我究竟做错了什么?为什么下面我的非常简单的教学示例不起作用?

var context = this;

var newcontact = new Contact(){
    FirstName = "Julio",
    LastName = "Bartowski",
    Title = "Mr.",
    AddDate = DateTime.Now,
    ModifiedDate = DateTime.Now
};
newcontact.Dump();  //show the entity object before saving to the database

context.AddToContacts(newcontact);
context.SaveChanges();

newcontact.Dump();    //show the entity object after saving to the database

根据 Julia 的书,newcontact 应该在 SaveChanges() 之后更新,从 EntityState "Added" 到 "Unchanged" 并加载新的主键。不幸的是,上面的第一个 newcontact.Dump() 显示了与最终的 newcontact.Dump() 相同的内容: ContactID = 0

Contact 
BAGA.Contact 
<dl><dt>ContactID</dt><dd>0</dd></dl> 
FirstName Julio 
LastName Bartowski 
Title null  
AddDate 5/27/2012 8:07:11 PM 
ModifiedDate 5/27/2012 8:07:11 PM 
RowVersion null  

运行上述命令后查看 SQL Server Management Studio 显示 ContactID = 0 并非实际发生的情况。实际上有一个带有新 ContactID 的插入。未更新/刷新新联系人实体的 ContactID 属性。

鉴于上述问题,添加依赖地址插入(使用如下所示的两种语法)需要有效的 ContactID,当然也会失败。我读过的所有内容都说:“您可以使用服务器生成的值创建任意复杂的实体图,并且系统可以在一次调用 SaveChanges 中处理这些整个图的插入”。嗯...我不能。

var context = this;

var newcontact = context.Contact.CreateContact(0, "Julio", "Bartowski", DateTime.Now, DateTime.Now, null);

var nAddr = Address.CreateAddress(0, "Home", 0, DateTime.Now, null);
nAddr.Contact = newcontact;
nAddr.Dump();

var newaddress = new Address(){
    Street1 = "5907 Hollow Oak Ct",
    City = "Burke",
    StateProvince = "VA",
    CountryRegion = "US",
    PostalCode = "22015",
    AddressType = "Home",
    ModifiedDate = DateTime.Now
};
newaddress.Contact = newcontact;

this.AddToContacts(newcontact);  //I get an error here when new Addresses are waiting for the ContactID value.
this.SaveChanges();
newcontact.Dump();

LinqPad 给了我一个更新异常:更新条目时发生错误。有关行 context.AddToContacts(newcontact); 的详细信息,请参见内部异常。

原始值 InvalidOperationException 表示此 ObjectStateEntry 没有原始值。处于添加或分离状态的对象不能具有原始值。

RuntimeMethodInfo 是 EntityEntry.InternalGetOriginalValues (Boolean readOnly) Name InternalGetOriginalValues DeclaringType typeof (EntityEntry) ReflectedType
typeof (EntityEntry)
MemberType Method MetadataToken 100678288 Module 4RuntimeModule 4
System.Data.Entity.dll

IsSecurityCritical False IsSecuritySafeCritical False IsSecurityTransparent True MethodHandle 4RuntimeMethodHandle 4
System.RuntimeMethodHandle

属性 PrivateScope、Private、HideBySig CallingConvention Standard、HasThis ReturnType typeof (DbDataRecord)
ReturnTypeCustomAttributes 4RuntimeParameterInfo 4
System.Data.Common.DbDataRecord

ReturnParameter 4RuntimeParameterInfo 4
System.Data.Common.DbDataRecord

IsGenericMethod False IsGenericMethodDefinition False ContainsGenericParameters False MethodImplementationFlags IL IsPublic False IsPrivate True IsFamily False IsAssembly False IsFamilyAndAssembly False IsFamilyOrAssembly False IsStatic False IsFinal False IsVirtual False IsHideBySig True IsAbstract False IsSpecialName False IsColleConstructor 4 False CustomAttributes 4ReadOnly

拜托,有人对我的“简单”问题有一个他们知道有效的简单答案。

4

0 回答 0