EntityFramework Model-breezejs的第一个元数据 [英] EntityFramework Model-First metadata for breezejs

查看:223
本文介绍了EntityFramework Model-breezejs的第一个元数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Breeze.js库需要实体上下文的元数据。 Web API OData有一个默认的ODataConventionModelBuilder用于此操作,但不适用于Breeze,因为它缺少外键信息。因此,Breeze提供了一个名为EdmBuilder的特殊包来生成这些信息。但是,它只适用于Code-First方法。如果有现有的edmx文件,它会提供以下异常:


创建DbModelBuilder或从DbContext创建EDMX创建的
不支持使用Database First或Model First。 EDMX只能从未使用现有
DbCompiledModel的Code First DbContext获取


如果项目中有一个现有的edmx文件,可以如何将其作为元数据信息发布到breezejs?

解决方案

这个信息将在运行时完成,它必须是关于读取加载的资源。当我想弄清楚,我发现这个链接;
https://gist.github.com/dariusclay/8673940



唯一的问题是正则表达式模式对我的连接字符串不起作用。但是,在确定之后,它产生了微风所寻找的信息。



最后,我将微风的Code-First和Model-First方法以下课(肯定可以改进)。希望对别人有用。



更新



确定DBContext是Code-First还是Model-First。

 使用Microsoft.Data.Edm.Csdl; 
使用Microsoft.Data.Edm.Validation;
使用系统;
使用System.Collections.Generic;
使用System.Data.Entity;
使用System.Data.Entity.Core.EntityClient;
使用System.Data.Entity.Infrastructure;
使用System.Diagnostics;
使用System.IO;
使用System.Reflection;
使用System.Text.RegularExpressions;
使用System.Xml;

命名空间Microsoft.Data.Edm
{
///< summary>
/// DbContext扩展,从< see cref =DbContext/>构建实体数据模型(EDM)
///< / summary>
///< remarks>
///我们需要EDM来定义Web API OData路由和Breeze客户端的
///元数据源。
///< p>
/// Web API OData文献推荐
///< see cref =System.Web.Http.OData.Builder.ODataConventionModelBuilder/> ;.
///该组件对于路由定义很有帮助,但是作为Breeze的
///元数据的来源失败,因为(在撰写本文时)它忽略包含
/// foreign Breeze要求的关键定义是维护客户端JavaScript实体的导航属性
///。
///< / p>< p>
///这个EDM Builder要求EF DbContext提供
///满足路由定义和Breeze的元数据。
///< / p>< p>
///这个类可以下载和安装为一个nuget包:
/// http://www.nuget.org/packages/Breeze.EdmBuilder/
///< ; / p为H.
///< / remarks>
public static class EdmBuilder
{
///< summary>
///从< see cref =DbContext/> ;.构建实体数据模型(EDM)。
///< / summary>
///< typeparam name =T>源的类型< see cref =DbContext/>< / typeparam>
///< returns> XML< see cref =IEdmModel/>。< / returns>
///< example>
///<![CDATA [
/// / *在WebApiConfig.cs * /
/// config.Routes.MapODataRoute(
/// routeName :odata,
/// routePrefix:odata,
/// model:EdmBuilder.GetEdmModel< DbContext>(),
/// batchHandler:new DefaultODataBatchHandler(GlobalConfiguration。 DefaultServer)
///);
///]]>
///< / example>
public static IEdmModel GetEdmModel< T>()其中T:DbContext,new()
{
return GetEdmModel< T(new T());
}

///< summary>
///扩展方法从
/// existing< see cref =DbContext/> ;.构建实体数据模型(EDM)。
///< / summary>
///< typeparam name =T>源的类型< see cref =DbContext/>< / typeparam>
///< param name =dbContext>具体< see cref =DbContext/>用于EDM生成。< / param>
///< returns> XML< see cref =IEdmModel/>。< / returns>
///< example>
/// / *在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)其中T:DbContext,new()
{
dbContext = dbContext?新T();

//获取内部上下文
var internalContext = dbContext.GetType()。GetProperty(INTERNALCONTEXT,BindingFlags.Instance | BindingFlags.NonPublic).GetValue(dbContext);

//代码第一个模型?
var isCodeFirst = internalContext.GetType()。GetProperty(CODEFIRSTMODEL).GetValue(internalContext)!= null;

//返回基于dbcontext类型的结果
返回isCodeFirst
? GetCodeFirstEdm< T>(dbContext)
:GetModelFirstEdm< T>(dbContext);
}


///< summary>
/// [OBSOLETE]从现有的< see cref =DbContext/>构建实体数据模型(EDM)
///使用Code-First创建。使用< see cref =GetCodeFirstEdm/>代替。
///< / summary>
///< remarks>
///这个方法直接委托给< see cref =GetCodeFirstEdm/>其
///名称更好地描述了其目的和特异性。
///不赞成向后兼容。
///< / remarks>
[Obsolete(此方法已过时,使用GetEdmModel)]
public static IEdmModel GetEdm< T>(此T dbContext)其中T:DbContext,new()
{
return GetEdmModel< T>(dbContext);
}

///< summary>
/// [OBSOLETE]从< see cref =DbContext/>构建实体数据模型(EDM)使用Code-First创建。
///使用< see cref =GetModelFirstEdm/>对于Model-First DbContext。
///< / summary>
///< typeparam name =T>源的类型< see cref =DbContext/>< / typeparam>
///< returns> XML< see cref =IEdmModel/>。< / returns>
///< example>
///<![CDATA [
/// / *在WebApiConfig.cs * /
/// config.Routes.MapODataRoute(
/// routeName :odata,
/// routePrefix:odata,
/// model:EdmBuilder.GetCodeFirstEdm< CodeFirstDbContext>(),
/// batchHandler:new DefaultODataBatchHandler(GlobalConfiguration。 DefaultServer)
///);
///]]>
///< / example>
[Obsolete(此方法已过时,使用GetEdmModel)]
public static IEdmModel GetCodeFirstEdm&T;()其中T:DbContext,new()
{
return GetCodeFirstEdm(new T());
}

///< summary>
/// [OBSOLETE]扩展方法从
/// existing< see cref =DbContext/>构建一个实体数据模型(EDM)使用Code-First创建。
///使用< see cref =GetModelFirstEdm/>对于Model-First DbContext。
///< / summary>
///< typeparam name =T>源的类型< see cref =DbContext/>< / typeparam>
///< param name =dbContext>具体< see cref =DbContext/>用于EDM生成。< / param>
///< returns> XML< see cref =IEdmModel/>。< / returns>
///< example>
/// / *在WebApiConfig.cs * /
/// using(var context = new TodoListContext())
/// {
/// config。 Routes.MapODataRoute(
/// routeName:odata,
/// routePrefix:odata,
/// model:context.GetCodeFirstEdm(),
/ // batchHandler:new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)
///);
///}
///< / example>
[Obsolete(此方法已过时,使用GetEdmModel)]
public static IEdmModel GetCodeFirstEdm< T>(此T dbContext)其中T:DbContext,new()
{
using(var stream = new MemoryStream())
{
using(var writer = XmlWriter.Create(stream))
{
dbContext = dbContext ??新T();
System.Data.Entity.Infrastructure.EdmxWriter.WriteEdmx(dbContext,writer);
}
stream.Position = 0;
使用(var reader = XmlReader.Create(stream))
{
return EdmxReader.Parse(reader);
}
}
}

///< summary>
/// [OBSOLETE]从< see cref =DbContext/>构建实体数据模型(EDM)使用Model-First创建。
///使用< see cref =GetCodeFirstEdm/>为Code-First DbContext。
///< / summary>
///< typeparam name =T>源的类型< see cref =DbContext/>< / typeparam>
///< returns> XML< see cref =IEdmModel/>。< / returns>
///< example>
///<![CDATA [
/// / *在WebApiConfig.cs * /
/// config.Routes.MapODataRoute(
/// routeName :odata,
/// routePrefix:odata,
/// model:EdmBuilder.GetModelFirstEdm&ModelFirstDbContext>(),
/// batchHandler:new DefaultODataBatchHandler(GlobalConfiguration。 DefaultServer)
///);
///]]>
///< / example>
[Obsolete(此方法已过时,使用GetEdmModel)]
public static IEdmModel GetModelFirstEdm&T;()其中T:DbContext,new()
{
return GetModelFirstEdm(new T());
}

///< summary>
/// [OBSOLETE]扩展方法从< see cref =DbContext/>建立一个实体数据模型(EDM)使用Model-First创建。
///使用< see cref =GetCodeFirstEdm/>为Code-First DbContext。
///< / summary>
///< typeparam name =T>源的类型< see cref =DbContext/>< / typeparam>
///< param name =dbContext>具体< see cref =DbContext/>用于EDM生成。< / param>
///< returns> XML< see cref =IEdmModel/>。< / returns>
///< remarks>
///此方法的灵感和代码来自以下gist
///,它从Edmx文件中重载元数据:
/// https://gist.github.com / dariusclay / 8673940
///< / remarks>
///< example>
/// / *在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:不要多次处理对象)]
[已过时(此方法已过时,使用GetEdmModel)]
public static IEdmModel GetModelFirstEdm< T(这个T dbContext)其中T:DbContext,new()
{
dbContext = dbContext?新T();
使用(var csdlStream = GetCsdlResourceStream(dbContext))
{
using(var reader = XmlReader.Create(csdlStream))
{
IEdmModel模型;
IEnumerable< EdmError>错误;
如果(!CsdlReader.TryParse(new [] {reader},out model,out errors))
{
foreach(var e in errors)
Debug.Fail .ErrorCode.ToString(F),e.ErrorMessage);
}
返回模型;
}
}
}

静态流GetCsdlResourceStream(IObjectContextAdapter上下文)
{
//获取连接字符串构建器
var connectionStringBuilder = new EntityConnectionStringBuilder(context.ObjectContext.Connection.ConnectionString);

//从构建器的元数据属性获取正则表达式
var match = Regex.Match(connectionStringBuilder.Metadata,METADATACSDLPATTERN);

//获取资源名称
var resourceName = match.Groups [0] .Value;

//获取上下文组件
var assembly = Assembly.GetAssembly(context.GetType());

//返回csdl资源
return assembly.GetManifestResourceStream(resourceName);
}

//连接字符串元数据中查找概念模型名称的模式
const string METADATACSDLPATTERN =((\\\w + \\。)+ csdl) ;

// DbContext中的属性名称
const string INTERNALCONTEXT =InternalContext;

// InternalContext中的属性名称class
const string CODEFIRSTMODEL =CodeFirstModel;
}
}


Breeze.js library requires metadata of the entity context. Web API OData has a default ODataConventionModelBuilder for this operation, but doesn't work for Breeze, since it lacks foreign key information. Thus, Breeze offers a special package called "EdmBuilder" to generate this information. However, it only works with Code-First approach. If there is an existing edmx file, it gives the following exception;

Creating a DbModelBuilder or writing the EDMX from a DbContext created using Database First or Model First is not supported. EDMX can only be obtained from a Code First DbContext created without using an existing DbCompiledModel.

In short, if there is an existing edmx file in the project, how it can be published as metadata information to breezejs?

解决方案

Since generating this information will done in runtime, it had to be about reading the loaded resource. While I was trying to figure it out, I found this link; https://gist.github.com/dariusclay/8673940

The only problem was that regex pattern didn't work for my connection string. But after fixing that, it generated the information what breeze was looking for.

In the end, I've merged both breeze's Code-First and this Model-First methods in the following class (surely it can be improved). Hope it can be useful to someone else.

UPDATE

Now it also determines whether DBContext is Code-First or 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";
    }
}

这篇关于EntityFramework Model-breezejs的第一个元数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆