この情報の生成は実行時に行われるため、ロードされたリソースを読み取る必要がありました。理解しようとしているときに、このリンクを見つけました。
https://gist.github.com/dariusclay/8673940
唯一の問題は、接続文字列に対して正規表現パターンが機能しなかったことです。しかし、それを修正した後、そよ風が探していた情報が生成されました。
最終的に、breeze の Code-First メソッドとこの Model-First メソッドの両方を次のクラスにマージしました (確実に改善される可能性があります)。それが他の誰かに役立つことを願っています。
アップデート
また、DBContext が Code-First か Model-First かを判断するようになりました。
using Microsoft.Data.Edm.Csdl;
using Microsoft.Data.Edm.Validation;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.EntityClient;
using System.Data.Entity.Infrastructure;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml;
namespace Microsoft.Data.Edm
{
/// <summary>
/// DbContext extension that builds an "Entity Data Model" (EDM) from a <see cref="DbContext"/>
/// </summary>
/// <remarks>
/// We need the EDM both to define the Web API OData route and as a
/// source of metadata for the Breeze client.
/// <p>
/// The Web API OData literature recommends the
/// <see cref="System.Web.Http.OData.Builder.ODataConventionModelBuilder"/>.
/// That component is suffient for route definition but fails as a source of
/// metadata for Breeze because (as of this writing) it neglects to include the
/// foreign key definitions Breeze requires to maintain navigation properties
/// of client-side JavaScript entities.
/// </p><p>
/// This EDM Builder ask the EF DbContext to supply the metadata which
/// satisfy both route definition and Breeze.
/// </p><p>
/// This class can be downloaded and installed as a nuget package:
/// http://www.nuget.org/packages/Breeze.EdmBuilder/
/// </p>
/// </remarks>
public static class EdmBuilder
{
/// <summary>
/// Builds an Entity Data Model (EDM) from a <see cref="DbContext"/>.
/// </summary>
/// <typeparam name="T">Type of the source <see cref="DbContext"/></typeparam>
/// <returns>An XML <see cref="IEdmModel"/>.</returns>
/// <example>
/// <![CDATA[
/// /* In the WebApiConfig.cs */
/// config.Routes.MapODataRoute(
/// routeName: "odata",
/// routePrefix: "odata",
/// model: EdmBuilder.GetEdmModel<DbContext>(),
/// batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)
/// );
/// ]]>
/// </example>
public static IEdmModel GetEdmModel<T>() where T : DbContext, new()
{
return GetEdmModel<T>(new T());
}
/// <summary>
/// Extension method builds an Entity Data Model (EDM) from an
/// existing <see cref="DbContext"/>.
/// </summary>
/// <typeparam name="T">Type of the source <see cref="DbContext"/></typeparam>
/// <param name="dbContext">Concrete <see cref="DbContext"/> to use for EDM generation.</param>
/// <returns>An XML <see cref="IEdmModel"/>.</returns>
/// <example>
/// /* In the WebApiConfig.cs */
/// using (var context = new TodoListContext())
/// {
/// config.Routes.MapODataRoute(
/// routeName: "odata",
/// routePrefix: "odata",
/// model: context.GetEdmModel(),
/// batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)
/// );
/// }
/// </example>
public static IEdmModel GetEdmModel<T>(this T dbContext) where T : DbContext, new()
{
dbContext = dbContext ?? new T();
// Get internal context
var internalContext = dbContext.GetType().GetProperty(INTERNALCONTEXT, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(dbContext);
// Is code first model?
var isCodeFirst = internalContext.GetType().GetProperty(CODEFIRSTMODEL).GetValue(internalContext) != null;
// Return the result based on the dbcontext type
return isCodeFirst
? GetCodeFirstEdm<T>(dbContext)
: GetModelFirstEdm<T>(dbContext);
}
/// <summary>
/// [OBSOLETE] Builds an Entity Data Model (EDM) from an existing <see cref="DbContext"/>
/// created using Code-First. Use <see cref="GetCodeFirstEdm"/> instead.
/// </summary>
/// <remarks>
/// This method delegates directly to <see cref="GetCodeFirstEdm"/> whose
/// name better describes its purpose and specificity.
/// Deprecated for backward compatibility.
/// </remarks>
[Obsolete("This method is obsolete. Use GetEdmModel instead")]
public static IEdmModel GetEdm<T>(this T dbContext) where T : DbContext, new()
{
return GetEdmModel<T>(dbContext);
}
/// <summary>
/// [OBSOLETE] Builds an Entity Data Model (EDM) from a <see cref="DbContext"/> created using Code-First.
/// Use <see cref="GetModelFirstEdm"/> for a Model-First DbContext.
/// </summary>
/// <typeparam name="T">Type of the source <see cref="DbContext"/></typeparam>
/// <returns>An XML <see cref="IEdmModel"/>.</returns>
/// <example>
/// <![CDATA[
/// /* In the WebApiConfig.cs */
/// config.Routes.MapODataRoute(
/// routeName: "odata",
/// routePrefix: "odata",
/// model: EdmBuilder.GetCodeFirstEdm<CodeFirstDbContext>(),
/// batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)
/// );
/// ]]>
/// </example>
[Obsolete("This method is obsolete. Use GetEdmModel instead")]
public static IEdmModel GetCodeFirstEdm<T>() where T : DbContext, new()
{
return GetCodeFirstEdm(new T());
}
/// <summary>
/// [OBSOLETE] Extension method builds an Entity Data Model (EDM) from an
/// existing <see cref="DbContext"/> created using Code-First.
/// Use <see cref="GetModelFirstEdm"/> for a Model-First DbContext.
/// </summary>
/// <typeparam name="T">Type of the source <see cref="DbContext"/></typeparam>
/// <param name="dbContext">Concrete <see cref="DbContext"/> to use for EDM generation.</param>
/// <returns>An XML <see cref="IEdmModel"/>.</returns>
/// <example>
/// /* In the WebApiConfig.cs */
/// using (var context = new TodoListContext())
/// {
/// config.Routes.MapODataRoute(
/// routeName: "odata",
/// routePrefix: "odata",
/// model: context.GetCodeFirstEdm(),
/// batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)
/// );
/// }
/// </example>
[Obsolete("This method is obsolete. Use GetEdmModel instead")]
public static IEdmModel GetCodeFirstEdm<T>(this T dbContext) where T : DbContext, new()
{
using (var stream = new MemoryStream())
{
using (var writer = XmlWriter.Create(stream))
{
dbContext = dbContext ?? new T();
System.Data.Entity.Infrastructure.EdmxWriter.WriteEdmx(dbContext, writer);
}
stream.Position = 0;
using (var reader = XmlReader.Create(stream))
{
return EdmxReader.Parse(reader);
}
}
}
/// <summary>
/// [OBSOLETE] Builds an Entity Data Model (EDM) from a <see cref="DbContext"/> created using Model-First.
/// Use <see cref="GetCodeFirstEdm"/> for a Code-First DbContext.
/// </summary>
/// <typeparam name="T">Type of the source <see cref="DbContext"/></typeparam>
/// <returns>An XML <see cref="IEdmModel"/>.</returns>
/// <example>
/// <![CDATA[
/// /* In the WebApiConfig.cs */
/// config.Routes.MapODataRoute(
/// routeName: "odata",
/// routePrefix: "odata",
/// model: EdmBuilder.GetModelFirstEdm<ModelFirstDbContext>(),
/// batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)
/// );
/// ]]>
/// </example>
[Obsolete("This method is obsolete. Use GetEdmModel instead")]
public static IEdmModel GetModelFirstEdm<T>() where T : DbContext, new()
{
return GetModelFirstEdm(new T());
}
/// <summary>
/// [OBSOLETE] Extension method builds an Entity Data Model (EDM) from a <see cref="DbContext"/> created using Model-First.
/// Use <see cref="GetCodeFirstEdm"/> for a Code-First DbContext.
/// </summary>
/// <typeparam name="T">Type of the source <see cref="DbContext"/></typeparam>
/// <param name="dbContext">Concrete <see cref="DbContext"/> to use for EDM generation.</param>
/// <returns>An XML <see cref="IEdmModel"/>.</returns>
/// <remarks>
/// Inspiration and code for this method came from the following gist
/// which reates the metadata from an Edmx file:
/// https://gist.github.com/dariusclay/8673940
/// </remarks>
/// <example>
/// /* In the WebApiConfig.cs */
/// using (var context = new TodoListContext())
/// {
/// config.Routes.MapODataRoute(
/// routeName: "odata",
/// routePrefix: "odata",
/// model: context.GetModelFirstEdm(),
/// batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)
/// );
/// }
/// </example>
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Usage", "CA2202:Do not dispose objects multiple times" )]
[Obsolete("This method is obsolete. Use GetEdmModel instead")]
public static IEdmModel GetModelFirstEdm<T>(this T dbContext) where T : DbContext, new()
{
dbContext = dbContext ?? new T();
using (var csdlStream = GetCsdlResourceStream(dbContext))
{
using (var reader = XmlReader.Create(csdlStream))
{
IEdmModel model;
IEnumerable<EdmError> errors;
if (!CsdlReader.TryParse(new[] { reader }, out model, out errors))
{
foreach (var e in errors)
Debug.Fail(e.ErrorCode.ToString("F"), e.ErrorMessage);
}
return model;
}
}
}
static Stream GetCsdlResourceStream(IObjectContextAdapter context)
{
// Get connection string builder
var connectionStringBuilder = new EntityConnectionStringBuilder(context.ObjectContext.Connection.ConnectionString);
// Get the regex match from metadata property of the builder
var match = Regex.Match(connectionStringBuilder.Metadata, METADATACSDLPATTERN);
// Get the resource name
var resourceName = match.Groups[0].Value;
// Get context assembly
var assembly = Assembly.GetAssembly(context.GetType());
// Return the csdl resource
return assembly.GetManifestResourceStream(resourceName);
}
// Pattern to find conceptual model name in connecting string metadata
const string METADATACSDLPATTERN = "((\\w+\\.)+csdl)";
// Property name in DbContext class
const string INTERNALCONTEXT = "InternalContext";
// Property name in InternalContext class
const string CODEFIRSTMODEL = "CodeFirstModel";
}
}