04第四讲ADONET实体框架._第1页
04第四讲ADONET实体框架._第2页
04第四讲ADONET实体框架._第3页
04第四讲ADONET实体框架._第4页
04第四讲ADONET实体框架._第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第四讲 ADO.NET 实体框架长久以来,程序员和数据库总是保持着一种微妙的关系, 在商用应用程序中, 数据库一定是不可或缺的元件, 这让程序员一定要为了连接与访问数据库而去学 习 SQL 语句,然而在众多设计工具中,数据以对象的形式体现。 因此在业界中 有很多人 都在研究如何将 对象 模型 和数据库集 成在 一起 ,对象关系对应 (Object-Relational Mapping) 的 技 术 就 是 由 此 而 生 , 像 Hibernate 或 NHibernate都是这个技术下的产物,而微软虽然有了 ADO.NE这 个数据访问的 利器,但却没有像 NHibernate 这样的对象对应

2、工具, 因此微软在 .NET Framework 2.0 发展时期,就提出了一个 ObjectSpace 的概念, ObjectSpace 可以让应用程 序可以用完全对象化的方法连接与访问数据库, 其技术概念 与 NHibernate 相当 类似,然而 ObjectSpace 工程相当大,在 .NET Framework 2.0 完成时仍无法全 部完成,因此微软将 ObjectSpace 纳入下一版本的 .NET Framework 中,并且再 加上一个设计的工具 (Designer) ,构成了现在的 ADO.NET Entity Framework 。Entity Framework 利用了

3、抽象化数据结构的方式,将每个数据库对象都转 换成应用程序对象 (entity) ,而数据字段都转换为属性 (property) ,关系则转 换为结合属性 (association) ,让数据库的 E/R 模型完全的转成对象模型,如 此让程序设计师能用最熟悉的编程语言来调用访问。 而在抽象化的结构之下, 则 是高度 集成 与对应结 构的概 念层 、对应层 和储存 层, 以 及支持 Entity Framework 的数据提供者 (provider) ,让数据访问的工作得以顺利与完整的进 行。4.1 为概念模型赋予生命 数据建模的一种由来已久且常见的设计模式是将数据模型分为三个部分:概 念模型、逻

4、辑模型和物理模型。 概念模型定义要建模的系统中的实体和关系。 关 系数据库的逻辑模型通过外键约束将实体和关系规范化到表中。 物理模型通过指 定分区和索引等存储详细信息实现特定数据引擎的功能。物理模型由数据库管理员进行优化以改善性能, 而编写应用程序代码的程序 员的工作主要限制为通过编写 SQL 查询和调用存储过程来处理逻辑模型。概念 模型通常用作捕获和传达应用程序的要求的工具, 常常以静态关系图的形式供项 目早期阶段查看和讨论, 随后被弃用。 许多开发团队会跳过概念模型的创建, 直 接从指定关系数据库中的表、列和键开始工作。实体框架 , 可使开发人员查询概念模型中的实体和关系,同时依赖于实体框

5、 架 将这些操作转换为特定于数据源的命令,从而为概念模型赋予生命。 这使应 用程序不再对特定数据源具有硬编码的依赖性 。概念模型、 存储模型以及两个模 型之间的映射以外部规范(称为 实体数据模型(EDM)表示。可以根据需要对 存储模型和映射进行更改, 而不需要对概念模型、 数据类或应用程序代码进行更 改。存储模型是特定于提供程序的, 因此可以在各种数据源之间使用一致的概念 模型。EDM由以下三种模型和具有相应文件扩展名的映射文件进行定义。 概念架构定义语言文件 (.csdl) -定义概念模型。存储架构定义语言文件 (.ssdl) -定义存储模型(又称逻辑模型)。映射规范语言文件 (.msl)

6、- 定义存储模型与概念模型之间的映射。实体框架 使用这些基于 XML 的模型和映射文件将对概念模型中的实体和 关系的创建、读取、更新和删除操作转换为数据源中的等效操作。EDM甚至支持 将概念模型中的实体映射到数据源中的存储过程。4.2 将对象映射到数据 面向对象的编程对与数据存储系统的交互提出了一个难题。 虽然类的组织通 常可比较接近地反映出关系数据库表的组织, 但是拟合程度并不完美。 多个规范 化表通常对应于单个类,类之间的关系并未按照表之间的关系一样表示。例如, 若要表示某个销售订单的客户,一个 Order 类可使用包含对 Customer 类实例 的引用的属性,但是数据库中的一个 Ord

7、er 表行包含的一个外键列(或列集) 具有对应于 Customer 表中的主键值的值。一个 Customer 类可以具有名为 Orders 的属性,该属性包含 Order 类的实例的集合,但是数据库中的 Customer 表不包含相应的列。现有解决方案只能通过将面向对象的类和属性映射到关系表和列来尝试弥 合这种通常称为“阻抗不匹配”的差异。实体框架 没有采用这种传统方法,而 是将逻辑模型中的关系表、 列和外键约束映射到概念模型中的实体和关系。 这在 定义对象和优化逻辑模型方面都增加了灵活性。实体数据模型 工具基于概念模 型生成可扩展数据类。 这些类是分部类, 可以通过开发人员添加的其他成员进行

8、 扩展。为特定概念模型生成的类派生自一些基类, 这些基类提供对象服务以将实 体具体化为对象以及跟踪和保存更改。 开发人员可以使用这些生成的类以由导航 属性关联起来的对象的形式来处理实体和关系。4.3 访问和更改实体数据实体框架不仅仅是另一种对象关系映射解决方案, 还从根本上使应用程序可 以访问和更改表示为概念模型中的实体和关系的数据。对象服务使用EDM 将对概念模型中所表示的实体类型的对象查询转换为特定于数据源的查询。 查询结果 具体化为对象服务管理的对象。 实体框架 提供以下方式用于查询 EDM 并返回对 象:LINQ to Entities - 提供语言集成查询 (LINQ) 支持用于查询

9、在概念 模型中定义的实体类型。Entity SQL- 与存储无关的 SQL 方言,直接使用概念模型中的实体并 支持诸如继承和关系等 EDM 功能。 Entity SQL 可用于对象查询和使用 EntityClient 提供程序执行的查询。查询生成器方法 - 可以使用 LINQ 风格的查询方法构造 Entity SQL 查询。实体框架 中包含 EntityClient 数据提供程序。此提供程序管理连接,将 实体查询转换为特定于数据源的查询, 并返回一个由对象服务用来将实体数据具 体化为对象的数据读取器。 当不需要对象具体化时, 通过使应用程序执行 Entity SQL 查询并使用返回的只读数据读

10、取器,还可以像标准 ADO.NET 数据提供程序一样使用 EntityClient 提供程序。 下图说明实体框架的使用过程:EntityLINQ10EnuiTierablefn 划Entidw”t、对靈肌务EDMEntitySQIJ也询LEntityrEntityChent朝誓抚供程序DBOatdRederADO.NET fitH摄供程序实体框架生成一个从ObjectC on text派生的类,该类表示概念模型中的实 体容器。此对象上下文提供跟踪更改以及管理标识、并发和关系的功能。此类 还公开将插入、更新和删除操作写入数据源的 SaveChanges方法。与查询类似, 这些更改是由系统自动生成

11、的命令或由开发人员指定的存储过程执行的。下面通过一个示例来说明实体数据框架的使用。一、创建School数据库和架构1. 在“文件”菜单上,指向“新建”,然后单击“数据库引擎查询”。2. 在“连接到数据库引擎”对话框中,键入localhost 或本地SQL Server 实例的名称,然后单击“连接”。3. 在查询窗口中粘贴以下Transact-SQL脚本,然后单击“执行”。(数据 库脚本见附件)二、创建Course Manager应用程序1. 在“文件”菜单上单击“新建项目”。2. 在“项目类型”窗格中,选择“ Visual Basic ”或“Visual C# ”。3. 在“模板”窗格中,选择

12、“ Windows窗体应用程序”。.4.、.5.6.对于项目名称输入CourseManager,然后单击“确定”。 在 CourseManager 项目中,选择默认窗体 (Form1) 。 在“文件属性”窗格中,将“文件名”更改为CourseViewer.vb 或CourseViewer.cs 。在 “ 解 决方 案 资源 管 理 器 ” 中 , 双 击 CourseViewer.vb 或 CourseViewer.cs 以打开窗体。在“属性”窗格中,将 Name 属性更改为 CourseViewer ,并将 Text 属 性更改为

13、 Course Viewer 。在“工具箱”中, 展开“公共控件”, 将“复合框”控件拖到窗体上, 然 后将该控件的名称更改为 departmentList 。在“工具箱”中,将“按钮”控件拖到窗体上,将此控件的名称更改为 closeForm ,并将 Text 值更改为 Close 。 在“工具箱”中,展开“数据”,将“ DataGridView ”控件拖到窗体上, 然后将该控件的名称更改为 courseGridView 。 双击 closeForm 按钮控件。此时将打开窗体的代码页并创建 closeForm_Click 事件处理程序方法。 在 closeForm_Click 事件处理程序方法

14、中,键入以下用于关闭窗体的代 码:/ Close the form. this.Close();生成 School 实体数据模型在“解决方案资源管理器”中选择 CourseManager 项目,右键单击,指 向“添加”,然后单击“新建项”。在“模板”窗格中,选择“ ADO.NET实体数据模型”。 为模型名称键入 School.edmx ,然后单击“添加”。 将显示“实体数据模型向导”的开始页。在“选择模型内容”对话框中, 选择“从数据库生成”。 然后,单击“下单击“新建连接”按钮7. 在“选择数据源”对话框中,选择您的数据源,然后单击“继续”。8. 在“连接属性”对话框中, 输入服务器名称,

15、选择身份验证方法, 对于数 据库名称,键入 School ,然后单击“确定”。9. 将使用您的数据库连接设置更新“选择您的数据连接”对话框。10. 确保选中“将 App.Config 中的实体连接设置保存为 : ”,并且值设置为 SchoolEntities 。然后,单击“下一步”。11. 将显示“选择数据库对象”对话框。12. 确保选中所有表和存储过程,且“模型命名空间”的值为SchoolModel ,然后单击“完成”以完成向导。13. 在“解决方案资源管理器”中,双击School.edmx 文件。此时将在“ ADO.NET实体数据模型设计器”窗口中显示School模型。四、 查询实体和关联

16、在 School 数据库中查询系1. 在 CourseViewer 窗体的代码文件的开始处,添加以下 using (C#) 或 Imports (Visual Basic) 语句,以引用从 School 数据库和实体命名空 间中创建的模型。using System.Data.Objects;using System.Data.Objects.DataClasses;2. 在 CourseViewer 窗体的分部类定义的顶部,添加以下用于创建 ObjectContext 实例的代码。/ Create an ObjectContext instance based on SchoolEntity.

17、private SchoolEntities schoolContext;3. 在 CourseViewer 窗体设计器中, 双击 CourseViewer 窗体。此时将打开 窗体的代码页并创建 courseViewer _Load 事件处理程序方法。4. 在 courseViewer _Load 事件处理程序方法中, 复制并粘贴以下用于定义DataGridView 的代码,执行返回系科集合的查询(按Name 排序),然后将 Department 对象的集合绑定到 departmentList控件。/ Initialize the ObjectContext.schoolContext = n

18、ew SchoolEntities();/ Define a query that returns all Department objects and related/ Course objects, ordered by name./ 查询生成器方法 ObjectQuery departmentQuery =schoolContext.Department.Include(Course).OrderBy(it.Name); /* 特别注意 include 的用法,后面的课程中将详细介绍 * /LINQ to Entities语法var departmentQuery1 = from dep

19、artment in schoolContext.Department.Include(Course) orderby department.Name select department;try / Bind the ComboBox control to the query, which is/ executed during data binding. this.departmentList.DataSource = departmentQuery1; this.departmentList.DisplayMember = Name;catch (Exception ex) Message

20、Box.Show(ex.Message);显示所选系的课程1. 在 CourseViewer 窗体设计器中, 双击 departmentList 控件。此时将创 建 departmentList_SelectedIndexChanged 事件处理程序方法。2. 粘贴以下用于加载与所选系相关的课程的代码。try/ Get the object for the selected department. Department department =(Department)this.departmentList.SelectedItem;/ Bind the grid view to the col

21、lection of Course objects/ that are related to the selected Department object. courseGridView.DataSource = department.Course; courseGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMo de.AllCells);catch (Exception ex) MessageBox.Show(ex.Message);五、 插入和更新数据保存对对象所做的更改1. 在“工具箱”中,展开“公共控件”, 将“按钮”控件拖

22、到 CourseViewer 窗体设计器,将控件的名称更改为 saveChanges,并将Text值更改为 Update。2. 在 CourseViewer 窗体设计器中,双击 saveChanges 控件。3. 此时将创建 saveChanges_Click 事件处理程序方法。4. 粘贴以下代码,这些代码将对象更改保存到数据库中。tryint numChanges;/ Save object changes to the database, display a message,/ and refresh the form.numChanges = schoolContext.SaveChan

23、ges();MessageBox.Show(numChanges.ToString() + change(s) saved to the database.);this.Refresh();catch (Exception ex)MessageBox.Show(ex.Message);通过释放长时间运行的对象上下文关闭连接在 closeForm_Click 事件处理程序方法中,键入以下代码。此代码在关闭窗体之前释放对象上下文。/ Dispose the object context.schoolContext.Dispose();4.4 对象服务对象服务是 实体框架 的一个组件,您可使用它来查

24、询、插入、更新和删除 数据,它表示为作为实体类型实例的强类型 CLR 对象。对象服务支持针对 实体 数据模型 (EDM) 中定义的类型进行的语言集成查询 (LINQ) 和 Entity SQL 查 询。对象服务将返回的数据具体化为对象, 并将对象更改传播回数据源。 它还提 供了跟踪更改、将对象绑定到控件以及处理并发问题的功能。对象服务由 System.Data.Objects 和 System.Data.Objects.DataClasses 命名空间中的类 来实现。对象上下文ObjectContext 类是以对象(这些对象是 EDM 中定义的实体类型的实例) 的形式与数据进行交互的主要类。

25、ObjectContext 类的实例封装以下内容: 到数据库的连接,以 EntityConnection 对象的形式封装。 描述模型的元数据,以 MetadataWorkspace 对象的形式封装。 在创建、更新和删除操作过程中跟踪对象的 ObjectStateManager 对象。本讲重点描述如何利用对象服务完成对象的添加、修改、删除及并发控制, 至于如何利用对象服务完成查询,我们放在下一讲。对象上下文中的对象是实体类型的实例,表示数据源中的数据。在对象上下 文中可以修改、 创建和删除对象, 对象服务会跟踪对这些对象所做的更改。 调用 SaveChanges 方法时,对象服务会生成并执行一些

26、命令, 这些命令将对数据源执 行等效的插入、更新或删除语句。下面的程序基于 AdventureWorks 销售 实体 数据模型,请按以下步骤创建。1. 在“项目”菜单上单击“添加新项” 。2. 在“模板”窗格中,选择“ADO.NET实体数据模型”。3. 键入 AdventureWorks.edmx 作为模型名称,然后单击 “添加” 。 将显示“实体数据模型向导”的第一页。4. 在“选择模型内容” 对话框中, 选择“从数据库生成” 。然后,单击“下5. 单击 “新建连接” 按钮。6. 在“连接属性” 对话框中, 键入服务器名称, 选择身份验证方法, 对于数 据库名称,键入 AdventureWo

27、rks ,然后单击 “确定” 。“选择您的数据连接” 对话框将以您的数据库连接设置更新。7. 确保“将 App.Config 中的实体连接设置另存为 :” 复选框已选中,并且 其值设置为 AdventureWorksEntities 。然后,单击 “下一步” 。8. 在“选择数据库对象” 对话框中, 清除所有对象, 展开“表” ,然后选择 以下表对象:AddressContactProductSalesOrderHeaderSalesOrderDetail9. 单击 “完成” 以完成向导。4.4.1 添加、删除、修改对象在本示例 中,对象查询根据指定的 SalesOrderID 返回单个 Sa

28、lesOrderHeader 对象。此订单的状态从 5 (已发货)更改为 1 (在制品), 向订单中添加一个新项,并删除第一个现有项。将调用 SaveChanges 方法以将 更改写到数据库中。然后,将订单的结果状态写到控制台。/ Specify the order to orderId = 43680;using (AdventureWorksEntities context = new AdventureWorksEntities()trySalesOrderHeader order =(from oh in context.SalesOrderHeaderwhere

29、 oh.SalesOrderID = orderId select oh).First();/ Change the status and ship date of an existing order.order.Status = 1; order.ShipDate = DateTime.Today;/ Load items for the order, if not already loaded./ 显式加载和 SalesOrderHeader 对象相关的 SalesOrderDetail 对象,两者的关系/ 为 1 对多的关系if (!order.SalesOrderDetail.IsLo

30、aded) order.SalesOrderDetail.Load();/ Delete the first item in the order. context.DeleteObject(order.SalesOrderDetail.First();/ Create a new item using the static Create method/ and add it to the order./ 实体框架 工具使用概念性架构定义语言(CSDL) 文件以生成用于定义对象/ 层的代码。当生成数据类时,将使用静态create 工厂方法生成每个类。此/ 方法用于实例化对象并设置此类的不能为空的

31、所有属性。 此方法对于在 CSDL/ 文件中已应用 Nullable=false 属性 (Attribute) 的每个属/(Property) 都具有一个参数。 当创建具有许多必需属性的对象时使用此方法。 order.SalesOrderDetail.Add(SalesOrderDetail.CreateSalesOrderDetail(0,0, 2, 750, 1, (decimal)2171.2942, 0, 0,Guid.NewGuid(), DateTime.Today);/ Save changes in the object context to the

32、changes = context.SaveChanges();Console.WriteLine(changes.ToString() + changes saved!);Console.WriteLine(Updated item for order: + order.SalesOrderID.ToString();foreach (SalesOrderDetail item in order.SalesOrderDetail.OrderBy(od=od.SalesOrderDetailID)Console.WriteLine(Item ID: + item.SalesOrderDetai

33、lID.ToString() + Product: + item.ProductID.ToString() + Quantity: + item.OrderQty.ToString();catch (UpdateException ex)Console.WriteLine(ex.ToString();4.4.2 对象间的关系 对象间的导航关系在建立一个新对象时,需要特别注意。例如,建立一个新的 SalesOrderHeader 对象,它本身和 Contract 对象存在多对一的关系,和 Address 对象存在多对一的关系, 和 SalesOrderDetail 对象存在一对多的关系。 因此在

34、添加一个新对象时, 要考虑和新对象关联的关系如何添加。 注意这些关系 在实体框架中体现为属性导航, 在数据库中则是实体间的外键关系。 实体框架一 般通过将子对象添加到父对象的 EntityConnection 集合来实现一对多的关系, 如:/ 在 SalesOrderHeader 中添加 SalesOrderDetail 对象 order.SalesOrderDetail.Add(item);/ 在 Contract 中添加 SalesOrderHeader 对象 customer.SalesOrderHeader.Add(order);多对一的关系怎样添加呢, 由于父对象一般会在子对象的实体

35、类中以一个属 性的方式实现,所有在只需要设置相应的属性就可以了。如:/ 在子对象 SalesOrderHeader 中设置父对象 Addressorder.Address = address;从上面的讲述可以观察到:customer.SalesOrderHeader.Add(order);和 order.Contact = customer; 语句是等效的。程序示例见附件代码中的 SaveObjectRelation() 方法。4.4.3 保存更改和管理并发实体框架 实现 开放式并发模型 。这意味着不对数据源中的数据保留锁。但 是默认情况下, 对象服务将对象更改保存到数据库中, 而不检查并发。

36、 对于可能 高度并发的属性 (Property) ,建议在概念层定义实体属性 (Property) ,并设置 属性 (Attribute) ConcurrencyMode=fixed. 在使用此属性时,对象服务会在 数据库中检查更改,然后将更改保存到数据库。任何有冲突的更改都会引发 OptimisticConcurrencyException 。知识点:什么是开放式并发控制 在多用户环境中,有两种用于更新数据库中数据的模型:开放式并发和保守 式并发。保守式并发涉及到锁定数据源中的行, 以防止其他用户因修改数据而影响当 前用户。 在保守式模型中,当用户执行会应用锁的操作时,其他用户将无法执 行可

37、能与锁发生冲突的操作,直到锁所有者释放锁为止。 此模型主要用于以下 环境:对数据存在激烈争用, 使得用锁保护数据的成本少于在发生并发冲突时回 滚事务的成本。因此,在保守式并发模型中,更新行的用户建立锁。 在该用户完成更新并 释放锁之前,其他任何用户都无法更改锁定行。 因此,如果锁定时间将会比较 短(例如在以编程方式处理记录时),最好实现保守式并发。 如果用户与数据 进行交互,会使记录锁定相对长的时间,保守式并发并不是可伸缩的选项。对比之下,使用开放式并发的用户在读取行时不会锁定该行。 当用户要更 新某行时,应用程序必须确定自读取该行以来,其他用户是否更改了该行。 开 放式并发通常用于对数据争用

38、较少的环境。 由于不需要锁定任何记录,开放式 并发将会提高性能,因为锁定记录需要更多的服务器资源。 另外,为了维护记录锁,需要与数据库服务器保持持久连接。由于在开放式并发模型中并不会这样,所以与服务器的连接可以在较少的时间内为更多的客户端提供服务。在开放式并发模型中,如果当某用户接收到来自数据库的值后,另一用户在 该用户试图修改该值之前即将其修改,则认为发生了冲突。以下各表是根据一个开放式并发示例生成的。下午1:00,用户1从具有以下值的数据库中读取一行:CustIDLastNameFirstName101SmithBob列名称原始值当前值数据库中的值CustID101101101LastNa

39、meSmithSmithSmithFirstNameBobBobBob下午1:01,用户2读取同一行。下午1:03,用户2将FirstName 从“ Bob更改为“ Robert ”并更新数据库。列名称原始值当前值数据库中的值CustID101101101LastNameSmithSmithSmithFirstNameBobRobertBob由于更新时数据库中的值匹配用户 2具有的原始值,因此更新成功。 下午1:05,用户1将“ Bob的名更改为“ James并试图更新该行。列名称原始值当前值数据库中的值CustID101101101LastNameSmithSmithSmithFirstNa

40、meBobJamesRobert此时,由于数据库中的值(“ JameS)不再匹配用户1所预期的原始值, 因此用户 1 遇到了开放式并发冲突。 并发冲突仅向您表明更新失败。 现在, 需要决定是用用户 1 提供的更改来重写用户 2 提供的更改还是取消用户 1 的 更改。在高度并发情况下进行更新时,建议经常调用 Refresh 。在调用 Refresh 时, RefreshMode 将控制传播更改的方式。如果使用 StoreWins 选项,则对象 服务将使用数据库中的相应值在对象缓存中重写所有数据。相反,如果使用 ClientWins 选项,则将使用数据源中的最新值替换缓存中的原始值。这样,通 过消

41、除缓存数据的更改与数据源中相应数据的更改之间的冲突, 可以确保对象缓 存中更改过的所有数据都可以成功地保存回数据源。对象服务跟踪已在缓存中对对象进行的更改。调用 SaveChanges 方法时, 对象服务尝试将更改合并回数据源。 如果对象缓存中的数据更改与对象添加到缓 存后或在缓存中刷新后的数据源更改发生冲突,SaveChanges 会失败,并引发OptimisticConcurrencyException 。 这 会 导 致 整 个 事 务 回 滚 。 如 果 发 生 OptimisticConcurrencyException ,应通过调用 Refresh 并指定是否解决冲突 (通过将数据

42、保存到对象数据 (ClientWins) 或通过使用数据源数据更新对象 缓存 (StoreWins) )来处理该异常,如下面的示例所示:try/ Try to save changes, which may cause a num = context.SaveChanges();Console.WriteLine(No conflicts. +num.ToString() + updates saved.);catch (OptimisticConcurrencyException)/ Resolve the concurrency conflict by refreshing the/ object context before re-saving changes. context.Refresh(RefreshMode.ClientWins, orders);/ Save changes.context.SaveChanges();Console.WriteLine(OptimisticConcurrencyException + handled and changes saved);以下示例说明实体框架处理开放式并发的过

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论