正如我所看到的,有一个接口公开了返回IObjectSet< ...>的方法,但是T4模板生成了返回ObjectSet< ...>的方法。并且没有生成的界面,并且在该页面上作者为创建的上下文添加了界面,并且它为他提供了创建模拟等的方法。
我的主要目标是使用T4模板生成poco类并创建模拟/伪上下文来测试我的自定义存储库。有没有办法让它无需编写或更改T4模板?如果它返回ObjectSet而不是IObjectSets,我如何在上下文之上创建模拟(对于IObjectSet并不是一件容易的事)...
提前谢谢
答案 0 :(得分:3)
作者只是嘲笑存储库,而不是实体。 EntityFramework生成ObjectQueries,但是他包装它们并且他的存储库返回IObjectQueries。他这样做是为了让他可以轻松地模拟数据,然后在保存期间他只是验证实体。
如果您只是想创建一个“模拟”存储库,您可以创建自己的T4模板并迭代edmx文件并生成代码。但没有理由必须生成POCOS?它们已经存在,为什么需要重新创建它们?他将所有内容抽象为“通用”FakeObjectSet,所以真的没有那么多代码可以编写吗?
你想要产生这个:
public IObjectSet<Blog> Blogs
{
get
{
return _blogs ?? (_blogs = new FakeObjectSet<Blog>());
}
set
{
_blogs = value as FakeObjectSet<Blog>;
}
}
private FakeObjectSet<Blog> _blogs;
如果是这样,我猜你会花更多时间在T4上,那么你就会写它。
没有类声明的示例T4 ...您可以按照this blog
执行完整的t4<#
foreach (EntitySet set in container.BaseEntitySets.OfType<EntitySet>())
{
#>
public IObjectSet<<#=MultiSchemaEscape(set.ElementType, code)#>>
{
get{
return <#=code.FieldName(set)#> ?? ( <#=code.FieldName(set)#> = FakeObjectSet<<#=MultiSchemaEscape(set.ElementType, code)#>>("<#=set.Name#>"));
}
set{
<#=code.FieldName(set)#> = value as FakeObjectSet<<#=MultiSchemaEscape(set.ElementType, code)#>>("<#=set.Name#>");
}
}
private FakeObjectSet<<#=MultiSchemaEscape(set.ElementType, code)#>> <#=code.FieldName(set)#>;
<#
}
#>
哪会生成此代码:
public IObjectSet<Blogs>{
get{
return _Blogs?? ( _Blogs = FakeObjectSet<Blog>("Blogs"));
}
set{
_Blogs= value as FakeObjectSet<Class>("Blogs");
}
}
private FakeObjectSet<Blog> _Blogs;
旁注。
IObjectSet包含在System.Data中,因此添加对System.Data.Entity.dll的引用
答案 1 :(得分:1)
Roy Osherove在The Art Of Unit Testing引用:
没有面向对象的问题,通过添加一个间接层无法解决,当然,除了间接层太多外。
以下是我的可模拟EF4 POCO设置。我没有使用T4,因为很难弄清楚如何清理模板以不产生太多的gumpf。你当然可以破解T4模板来吐出类似这种结构的东西。
诀窍是手动创建ObjectSet<T>
并将其公开为IQueryable
。由于Add
和Create
位于ObjectSet<T>
/ ObjectSet<T>
,我还必须添加添加和创建实体的方法。
public interface IStackTagzContext {
IQueryable<Question> Questions { get; }
Question CreateQuestion();
void CreateQuestion(Question question);
void SaveChanges();
}
public class StackTagzContext : ObjectContext, IStackTagzContext {
public StackTagzContext() : base("name=myEntities", "myEntities")
{
base.ContextOptions.LazyLoadingEnabled = true;
m_Questions = CreateObjectSet<Question>();
}
#region IStackTagzContext Members
private ObjectSet<Question> m_Questions;
public IQueryable<Question> Questions {
get { return m_Questions; }
}
public Question CreateQuestion() {
return m_Questions.CreateObject();
}
public void AddQuestion(Question question) {
m_Questions.AddeObject(question);
}
public new void SaveChanges() {
base.SaveChanges();
}
#endregion
}
现在,您将注意到界面上的实体集合类型为IQueryable<T>
,而不是IObjectSet<T>
。我无法为创建FakeObjectSet
和IQueryable
而烦恼,为我提供了足够的灵活性。所以为了KISS,我已经没有它了。
嘲弄IQueryable
是微不足道的:
using Moq;
[TestClass]
public class TestClass {
Mock<IStackTagzContext> m_EntitiesMock = new Mock<IStackTagzContext>();
[TestMethod()]
public void GetShouldFilterBySite() {
QuestionsRepository target = new QuestionsRepository(m_EntitiesMock.Object);
m_EntitiesMock.Setup(e=>e.Questions).Returns(new [] {
new Question{Site = "site1", QuestionId = 1, Date = new DateTime(2010, 06,23)},
}.AsQueryable());
}
}