数据库优先(和模型优先)方法有一个很好的DbContext生成器脚手架文件来生成上下文和模型;Model.Context.tt + Model.tt
因为它们有内置的助手方法来检索导航属性,所以将它们用于其他目的也很方便,比如创建控制器、视图等。ASP.NET Scaffolding也做了类似的工作,但在这种情况下,需要为每个模型调用scaffolder,相比之下,这些T4文件一次生成所有文件。
但是,它们只使用"edmx“文件作为输入。是否可以将它们用于代码优先方法?
实体框架6.1版
发布于 2014-04-29 01:55:31
以下是一个工作示例-更新于2018年6月5日:
https://github.com/coni2k/DbContextGeneratorWithCodeFirst
要实现这一点,需要几个步骤。
1.查找EF6.Utility.CS.ttinclude并将其复制到项目
在T4文件(Model.Context.tt + Model.tt)中,在两个位置使用的inputFile
变量(edmx文件);
const string inputFile = @"QAModel.edmx";
//var textTransform ...
//var code ...
//var ef ...
//var typeMapper ...
//var fileManager ...
var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile);
//var codeStringGenerator ...
if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile))
VerifyCaseInsensitiveTypeUniqueness
是一种验证方法,inputFile
用作错误的源位置。其中最重要的是EdmMetadataLoader
,它来自于文件开头定义的EF6.Utility.CS.ttinclude
文件;
<#@ include file="EF6.Utility.CS.ttinclude"#><#@
由于需要修改此文件,请找到该文件并复制到您的项目文件夹中。在我的例子中,它在这个文件夹下;
%ProgramFiles(x86)%\Microsoft Visual Studio 12.0\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\Templates\Includes\
如您所见,这是一个可选步骤。我们可以修改原始文件,但使用副本并保持原始文件不变会更安全。
2.修改ttinclude文件
在包含文件中,有三个使用edmx文件的方法。
public IEnumerable<GlobalItem> CreateEdmItemCollection(string sourcePath)
public XElement LoadRootElement(string sourcePath)
private void ProcessErrors(IEnumerable<EdmSchemaError> errors, string sourceFilePath)
重要的是读取xml文件的LoadRootElement
方法。我们可以从Code First DbContext创建一个内存流,并让它从这个流中读取数据,而不是传递一个物理xml。
a.将这两个方法添加到include文件中;
public IEnumerable<GlobalItem> CreateEdmItemCollection(DbContext dbContext)
{
ArgumentNotNull(dbContext, "dbContext");
var schemaElement = LoadRootElement(dbContext);
if (schemaElement != null)
{
using (var reader = schemaElement.CreateReader())
{
IList<EdmSchemaError> errors;
var itemCollection = EdmItemCollection.Create(new[] { reader }, null, out errors);
ProcessErrors(errors, dbContext.Database.Connection.ConnectionString);
return itemCollection ?? new EdmItemCollection();
}
}
return new EdmItemCollection();
}
public XElement LoadRootElement(DbContext dbContext)
{
ArgumentNotNull(dbContext, "dbContext");
XElement root;
using (var stream = new MemoryStream())
{
using (var writer = XmlWriter.Create(stream))
{
EdmxWriter.WriteEdmx(dbContext, writer);
}
stream.Position = 0;
root = XElement.Load(stream, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
root = root.Elements()
.Where(e => e.Name.LocalName == "Runtime")
.Elements()
.Where(e => e.Name.LocalName == "ConceptualModels")
.Elements()
.Where(e => e.Name.LocalName == "Schema")
.FirstOrDefault()
?? root;
}
return root;
}
现在我们在包含文件中使用DbContext
,这需要包含System.Data.Entity
库。
b.在文件的开头添加这三行;
//<#@ assembly name="%VS120COMNTOOLS%..\IDE\Microsoft.Data.Entity.Design.dll" #>
<#@ assembly name="System.Data.Entity" #>
<#@ import namespace="System.Data.Entity" #>
<#@ import namespace="System.Data.Entity.Infrastructure" #>
//<#@ import namespace="System" #>
3.通过添加新的构造函数来修改DbContext类
因为T4文件有自己的域,所以不能使用项目的web/app.config文件中的连接字符串来初始化DbContext
。最简单的解决方法是使用显式连接字符串对其进行初始化。
通过添加一个新的构造函数来修改您的DBContext类,该构造函数接受连接字符串作为参数。
public QAContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
4.修改T4文件
现在T4已经准备好传递一个Code First DbContext实例。相应地更新文件。
a.为了能够访问和实例化它,将DbContext
类库作为程序集引用添加到T4文件的开头;
//<#@ template language="C#" ...
<#@ assembly name="$(SolutionDir)DbContextGeneratorWithCodeFirst\bin\Debug\DbContextGeneratorWithCodeFirst.dll"#>
//<#@ include file="EF6 ...
b.用connectionString
替换inputFile
变量
const string connectionString = @"Server=(LocalDb)\v11.0;Database=QADb;Integrated Security=True;MultipleActiveResultSets=True";
//var textTransform ...
c.更新CreateEdmItemCollection
块
//var fileManager ...
IEnumerable<GlobalItem> itemCollection;
using (var dbContext = new DbContextGeneratorWithCodeFirst.QAContext(connectionString))
{
itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(dbContext);
if (itemCollection == null)
{
return string.Empty;
}
}
//var codeStringGenerator ...
d.使用connectionString
参数更新VerifyCaseInsensitiveTypeUniqueness
方法
if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), connectionString))
现在可以使用它了。您可以根据需要修改文件的其余部分,以基于Code First模型创建您想要的任何文件,如html、javascript、razor等。
这是一项工作量很大的工作,肯定可以改进。例如,包含文件可以接受DbContext
类型作为参数,实例化它,确定它是Code First还是Database First,然后继续处理。或者找到web/app.config文件并从中读取连接字符串。然而,对于一个开始来说,应该足够了。
https://stackoverflow.com/questions/23347093
复制相似问题