是的,您可以通过为上下文提供一个虚拟连接字符串来做到这一点。请注意,通常当您调用 DbContext 的无参数构造函数时,它会在主应用程序的 app.config 文件中查找具有您的上下文类名称的连接字符串。如果是这种情况并且您无法更改此行为(例如您不拥有相关上下文的源代码) - 您将必须使用该虚拟连接字符串更新 app.config(也可以在运行时完成)。如果可以使用连接字符串调用 DbContext 构造函数,则:
var cs = String.Format("metadata=res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl;provider=System.Data.SqlClient;provider connection string=\"\"", "TestModel");
using (var ctx = new TestDBEntities(cs)) {
var metadata = ((IObjectContextAdapter)ctx).ObjectContext.MetadataWorkspace;
// no throw here
Console.WriteLine(metadata);
}
所以你只提供获取元数据工作空间的重要参数,并提供空连接字符串。
更新:经过深思熟虑,您根本不需要使用这些技巧和实例化上下文。
public static MetadataWorkspace GetMetadataWorkspaceOf<T>(string modelName) where T:DbContext {
return new MetadataWorkspace(new[] { $"res://*/{modelName}.csdl", $"res://*/{modelName}.ssdl", $"res://*/{modelName}.msl" }, new[] {typeof(T).Assembly});
}
在这里,您只需直接使用 MetadataWorkspace 类的构造函数,将路径传递给目标元数据元素以及要检查的程序集。请注意,此方法做了一些假设:元数据工件被嵌入到资源中(通常它们是,但可以是外部的,或嵌入在其他路径下)并且所需的一切都与 Context 类本身在同一个程序集中(理论上你可能有一个程序集中的上下文和另一个程序集中的实体类,或其他东西)。但我希望你能明白。
UPDATE2:获取代码优先模型的元数据工作区有点复杂,因为该模型的 edmx 文件是在运行时生成的。它在哪里以及如何生成是实现细节。但是,您仍然可以通过一些努力获得元数据工作空间:
public static MetadataWorkspace GetMetadataWorkspaceOfCodeFirst<T>() where T : DbContext {
// require constructor which accepts connection string
var constructor = typeof (T).GetConstructor(new[] {typeof (string)});
if (constructor == null)
throw new Exception("Constructor with one string argument is required.");
// pass dummy connection string to it. You cannot pass empty one, so use some parameters there
var ctx = (DbContext) constructor.Invoke(new object[] {"App=EntityFramework"});
try {
var ms = new MemoryStream();
var writer = new XmlTextWriter(ms, Encoding.UTF8);
// here is first catch - generate edmx file yourself and save to xml document
EdmxWriter.WriteEdmx(ctx, writer);
ms.Seek(0, SeekOrigin.Begin);
var rawEdmx = XDocument.Load(ms);
// now we are crude-parsing edmx to get to the elements we need
var runtime = rawEdmx.Root.Elements().First(c => c.Name.LocalName == "Runtime");
var cModel = runtime.Elements().First(c => c.Name.LocalName == "ConceptualModels").Elements().First();
var sModel = runtime.Elements().First(c => c.Name.LocalName == "StorageModels").Elements().First();
var mModel = runtime.Elements().First(c => c.Name.LocalName == "Mappings").Elements().First();
// now we build a list of stuff needed for constructor of MetadataWorkspace
var cItems = new EdmItemCollection(new[] {XmlReader.Create(new StringReader(cModel.ToString()))});
var sItems = new StoreItemCollection(new[] {XmlReader.Create(new StringReader(sModel.ToString()))});
var mItems = new StorageMappingItemCollection(cItems, sItems, new[] {XmlReader.Create(new StringReader(mModel.ToString()))});
// and done
return new MetadataWorkspace(() => cItems, () => sItems, () => mItems);
}
finally {
ctx.Dispose();
}
}