我花费了大量时间来调整实体框架以满足我的需求,因此可以说它满足了您对 ORM 的大部分要求。但是有些方面过于复杂,因为其他 ORM 已经表明它可以变得更容易。
例如,开始使用 Entity Framework 相当容易,因为您只需在 Visual Studio 中启动设计器并在几分钟内就拥有一个正常工作的 ORM。但是您最终会得到与设计者创建的 ObjectContext 相关联的实体类(使用自定义 T4 模板可以避免这种情况)。这不一定是一件坏事,但它是那种你不想在实际应用程序中使用的 Microsoft“入门”方法。
但是,如果您更深入地研究实体框架,您会发现如何避免其中的大部分陷阱:Designer 生成一个 EDMX 文件,(如果您在 XML 编辑器中查看它)只不过是一个组合ORM 的三个主要方面,物理存储(您的数据库),概念模型(您的实体类)以及它们之间的映射。在 Visual Studio 中应用于 .edmx 文件的自定义生成操作会将这 3 个部分拆分为三个单独的文件,并将它们作为嵌入式资源添加到程序集中。创建 ObjectContext 时,这三个文件的路径在 ConnectionString 中使用(这对我来说总是有点混乱)。你在这里实际上可以做的,就是自己做这一切。这意味着在 XML 编辑器(很像 NHibernate)中编写存储模式、概念模型和映射,并将它们嵌入到包含您的模型的程序集中。
基础实体框架基类“ObjectContext”可以从这三个文件(它需要一个 MetadataWorkspace 和 EntityConnection)构建,但重点是,您可以完全控制如何创建 ObjectContext。这为实体框架提供的许多功能打开了大门。例如:您可以在同一个程序集中嵌入多个 SSDL 存储模式以匹配特定的数据库类型(我通常为 SQL Server 添加一个,为 SQL Server CE 4.0 添加一个)。并创建一个构造函数重载,为特定类型的 DbConnection 选择适当的存储模式。
既然你现在有了自己的 ObjectContext 实现,你可以在上面实现各种接口。就像你自己的 IRepository,但因为我喜欢 ObjectContext 方法,所以我创建了类似的东西:
interface ICatalog
{
IEntitySet<Article> { get; }
void Save();
}
interface IEntitySet<T> : IQueryable<T>
{
void Add(T);
void Remove(T);
}
class EntityFrameworkCatalog : ICatalog
{
...
}
但是,如果您有一个实体框架 ObjectContext,那么创建一个存储库真的很容易,而且您还可以获得一个 IQueryable。基于这些信息,您可以避免服务和 ORM 之间的强类耦合,并在测试中完全模拟实体框架。此外,在测试您的实体框架实现时,您可以在单元测试期间使用 SQL Server CE 数据库以确保您的映射正常(通常 CE 的存储架构与完整的 SQL Server 之间的差异只是一些数据-类型)。因此,您实际上可以很好地测试实体框架实现的所有行为。
这使得 Entity Framework 与现代软件概念很好地结合在一起,但它不会对您强制执行此类做法,这使得“入门”更容易。
现在回到复杂的部分:实体框架有一小部分受支持的 CLR 类型,它们基本上只包括原始类型,如整数、字符串和字节数组。它还提供了某种级别的复杂类型,它们遵循相同的规则。但是,如果您有一个复杂的实体属性,例如文档的 DOM 表示,您希望将其序列化为数据库中的 XML。据我所知,NHibernate 提供了一个名为 IUserType 的功能,它允许您为自己定义这样的映射。在实体框架中,这变得更加复杂,但它仍然以它自己的方式漂亮。概念模型允许您包含程序集内部的复杂类型(只要您将它告诉 ObjectContext (ObjectContext.CreateProxyTypes(Type[]))。因此您可以为您的原始类型创建一个包装器,它只知道像这样的实体框架:
class Document : IXmlSerializable { }
class Article
{
public virtual Document Content { get; set; }
}
internal class EntityFrameworkDocument : Document
{
public string Xml
{
get
{
// Use XmlSerializer to generate the XML-string for this instance.
}
set
{
// Use XmlSerializer to read the XML-string for this instance.
}
}
}
尽管 EF 现在可以从存储中返回那些序列化的文档,将它们写入其中,但需要您截取文章的存储并用 EntityFrameworkDocument 替换一个简单的文档,以确保 EF 可以对其进行序列化。我确信其他 ORM 很容易做到这一点,而且情况会变得更糟。目前没有办法对 System.Uri 类(它是不可变的,但可以正常工作)或枚举做同样的事情。除了这些限制之外,您还可以使 EF 满足您的大部分需求。但是你会花很多时间在上面(就像我一样)。
由于我对其他 ORM 的经验有限,我总结一下:
- 实体框架在 GAC 中,甚至在客户端配置文件中
- 可以自定义实体框架以表示甚至复杂的实体类型(包括一些自引用的多对多,例如上面的 XML 序列化)
- 它可以被“抽象”掉,所以你可以坚持使用 IRepository 等。
- IQueryable 实现(虽然它不像 DataObjects.Net 那样完整)
-
它只需要 System.Data 和 System.Data.Entity,您甚至可以为通常需要引用的其他提供者包含多个存储模式,但如果您坚持使用 DbConnection,您可以这样做:
ICatalog Create(DbConnection connection, string storageSchemaPath)
ICatalog CreateMySql(DbConnection mySqlConnection)
{
return Create(connection, "res://Assembly/Path.To.Embedded.MySql.Storage.ssdl");
}
编辑
我最近发现,如果您的实体和“目录”实现在同一个程序集中,您可以使用内部属性进行 XML 序列化过程。因此,与其从Document 派生内部EntityFrameworkDocument,不如向Document 类本身添加一个名为Xml 的内部属性。这仍然仅适用于您对实体具有完全控制权的情况,但它无需拦截对目录的任何更改,以确保使用您的派生类。 CSDL 看起来一样,EF 只允许映射属性是内部的。我仍然必须确保这可以在中等信任环境中工作。