我正在试图弄清楚是否有可能坚持“针对接口而不是实现的程序”的口头禅。使用Entity Framework 4.0。
虽然我找到了一个页面,解释了如何在使用Linq-to-SQL(look here)时坚持上述规则,但我非常想知道是否可以使用实体框架(也是实体框架)使用Linq)。
这是通常使用的方式:
var query = from pages in dataContext.Pages where pages.IsPublished select pages;
foreach (Page page in query)
{
// do something with page...
var routeQuery = from routes in page.Route where route.IsValid select routes;
foreach (Route route in routeQuery)
{
// do something with route
}
}
但我想像这样使用它:
var query = from pages in dataContext.Pages where pages.IsPublished select pages;
foreach (IPage page in query)
{
// do something with page...
var routeQuery = from routes in page.Route where route.IsValid select routes;
foreach (IRoute route in routeQuery)
{
// do something with route
}
}
基本上我希望能够通过使用接口将实体框架的DataContext传递出它所实现的程序集/子系统。数据上下文提供的所有信息都应以接口的形式出现,而不是实际的类。
我想保持实际的类在程序集内部实现实体,并且只公开它们实现的接口。
这是否可以通过实体框架实现?如果没有,是否有其他可以这样使用的O / R映射器?
如果这不是一个很好的方法,如何进一步将数据库与实际应用程序分离,我会热衷于听取您的建议。
答案 0 :(得分:6)
更好的解决方案(在我看来)是做以下事情:
为您的实体数据模型创建存储库,公开ICollection<T>
或IQueryable<T>
在您的存储库中使用接口:
public interface IRepository
{
public ICollection<Person> Find(string name); // tighter, more maintanability
public IQueryable<Person> Find(); // full power! but be careful when lazy loading
由于接口的原因,您可以使用其他ORM来换入模拟:
public class MockRepo : IRepository
{
public List<Person> persons; // mimics entity set
}
只有这么多你才能抽象出来。
如果您担心使用ObjectSet(与EF绑定),请使用POCO。
请查看我的其他一些问题以获取更多信息(因为我们现在正在构建此架构)。
另外,请考虑使用依赖注入。在这里,您可以从管理ObjectContext的业务中获取存储库 - 您可以将存储库注入工作单元(这是ObjectContext的包装器 - 因此多个存储库或聚合根可以处理相同的上下文)。
在我们的解决方案中,除了存储库之外,没有任何内容涉及实体框架(或任何持久性逻辑),这些存储库位于单独的程序集中。
HTH。
答案 1 :(得分:1)
您可以按照您提到的L2S示例中的大多数逻辑来实现基于接口的EF Repository类。一个主要的区别是Repository方法。它有点不同,因为EF不适用于Cast&lt;&gt;从一个IQueryable转换到另一个。我用反射来解决这个问题。
以下是为EF Repository类重写的Repository方法的示例:
public IQueryable<T> Repository<T>()
where T : class
{
if (typeof(T).IsInterface)
{
// if T is an interface then get the actual EntityObject Type by calling GetEntityType
// so that entityType can be used to call back to this method
Type entityType = this.GetEntityType<T>();
MethodInfo mi = this.GetType().GetMethod("Repository");
// set the T in Repository<T> to be the entity type
Type[] genericTypes = new Type[] { entityType };
mi = mi.MakeGenericMethod(genericTypes);
// call Repository<T>
object result = mi.Invoke(this, new object[0]);
return result as IQueryable<T>;
}
else
{
return this._context.CreateQuery<T>(this.GetEntitySetName<T>());
}
}
private Type GetEntityType<T>()
{
if (this.TableMaps.ContainsKey(typeof(T)))
{
return this.TableMaps[typeof(T)];
}
else
{
return typeof(T);
}
}
答案 2 :(得分:0)
您可以创建需要您的接口而不是具体对象的存储库,并且使用EF生成的类的部分类来实现这些接口。不过这是否真的值得付出努力,我不确定。
答案 3 :(得分:0)
我用EF 5.0实现了这个目标。解决方案太复杂,无法发布。我可以提供非常高级别的描述。
存储库的基类看起来像......
public class GenericRepository<TEntityInterface, TEntity>
where TDataInterface : TEntityInterface
where TEntity : class, IBaseEntityInterface, new()
此存储库的子类看起来像......
public class EmployeeRepository<TEntity> : GenericRepository<IEmployeeEntity, TEntity>, IEmployeeRepository
where TEntity : class, IEmployeeEntity, new()
我使用T4 templates来自定义实体的生成以继承实体接口,或者您可以为每个EF实体创建部分类以继承接口。我已经使用代码生成脚本为与实体属性相关的实体生成接口。所有实体都继承IBaseEntityInterface。
在您的代码中的某个时刻(在我的情况下使用INject注入框架),您将EF实体与存储库结合起来......
Bind<IEmployeeRepository>().To<EmployeeRepository<EmployeeEntity>();
实体框架生成EmployeeEntity的地方。
这种方法存在问题,实体框架不喜欢实体接口之间的LINQ连接,这样做会导致错误,具体取决于查询的结构。
您必须针对TEntity在存储库中执行查询。您可以在实体接口(以及存储库中的TEntity)上使用导航属性来有效地进行连接A-OK。
然而,有时您会想要在没有导航属性的情况下进行连接,解决方法是在存储库上公开返回原始IQueryable对象的方法,以便在其他存储库中使用,例如查询ID如IQueryable或代码IQueryable,创建这些在另一个存储库的存储库中,将它们包含在查询中。
我认为总的来说,在大型代码库中,使用实体接口而不是直接引用EF实体类的好处超过了使用EF与实体接口时遇到的问题。除了松耦合,更改的稳健性等之外,您可以在基础存储库中添加大量代码,并使用基础实体接口上的属性对TEntity进行编程。
例如,获取与谓词匹配的实体接口集合的方法,该谓词具有预先加载的多个属性
public IList<TEntityInterface> Where(Expression<Func<TEntityInterface, bool>> predicate, params string[] includedProperties)
{
DbQuery<TEntity> query = Context.Set<TEntity>();
foreach (string prop in includedProperties)
query = query.Include(prop);
return query.Where(predicate).ToList<TEntityInterface>();
}
就我而言,在代码中引用实体框架实体基本上是将代码严重耦合到EDMX,因此也就是特定的数据库模式。如果您需要支持具有相同代码库的多个数据库模式,则开箱即用的实体框架会让您非常高兴。
希望在未来的EF版本中,这些问题将得到解决,我们都可以成为优秀的小程序员,并且在没有这些技巧的情况下使用接口而不是EF类。