Có, bạn có thể thực hiện việc này bằng cách cung cấp ngữ cảnh chuỗi kết nối giả. Lưu ý rằng thường khi bạn gọi constructor parameterless của DbContext, nó sẽ tìm chuỗi kết nối với tên của lớp ngữ cảnh của bạn trong tệp app.config của ứng dụng chính. Nếu đó là trường hợp và bạn không thể thay đổi hành vi này (như bạn không sở hữu mã nguồn của ngữ cảnh đang được đề cập đến) - bạn sẽ phải cập nhật app.config với chuỗi conneciton giả đó (có thể được thực hiện trong thời gian chạy). Nếu bạn có thể gọi hàm tạo DbContext bằng chuỗi kết nối, thì:
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);
}
Vì vậy, bạn chỉ cung cấp thông số quan trọng để lấy không gian làm việc siêu dữ liệu và cung cấp chuỗi kết nối trống.
CẬP NHẬT: sau khi suy nghĩ thêm, bạn không cần phải sử dụng các hack như vậy và tạo bối cảnh ngay lập tức.
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});
}
Ở đây bạn chỉ cần sử dụng hàm tạo của lớp MetadataWorkspace trực tiếp, chuyển đường dẫn đến mục tiêu siêu dữ liệu và cũng được lắp ráp để kiểm tra. Lưu ý rằng phương thức này đưa ra một số giả định: các tạo phẩm siêu dữ liệu được nhúng vào các tài nguyên (thường là chúng, nhưng có thể ở bên ngoài hoặc được nhúng dưới một đường dẫn khác) và tất cả mọi thứ cần thiết trong cùng một assembly như lớp Ngữ cảnh (bạn có thể có lý thuyết) bối cảnh trong một hội đồng và các lớp thực thể khác, hoặc một cái gì đó). Nhưng tôi hy vọng bạn có được ý tưởng.
UPDATE2: để lấy không gian làm việc siêu dữ liệu của mô hình mã đầu tiên có phần phức tạp hơn, vì tệp edmx cho mô hình đó được tạo khi chạy. Vị trí và cách thức được tạo ra là chi tiết triển khai. Tuy nhiên, bạn vẫn có thể nhận được không gian làm việc siêu dữ liệu với một số nỗ lực:
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();
}
}
Làm thế nào để bạn khởi tạo 'ngữ cảnh '? –
@GertArnold Tôi chỉ sử dụng một hàm tạo parameterless – AlexFoxGill