如何使用WCF数据服务/ OData从sproc中使用复杂对象?

ElH*_*aix 5 entity-framework wcf-data-services c#-4.0

使用WCF数据服务(和最新的实体框架),我想从存储过程返回数据.返回的sproc字段与我的数据库中的任何实体都不匹配1:1,因此我在edmx模型中为它​​创建了一个新的复杂类型(而不是附加现有实体):

  1. 右键单击*.edmx模型/添加/功能导入
  2. 选择sproc(返回三个字段) - GetData
  3. 单击获取列信息
  4. 添加函数导入名称:GetData
  5. 单击"创建新的复杂类型 - GetData_Result"

在服务中,我定义:

    [WebGet]
    public List<GetData_Result> GetDataSproc()
    {
        PrimaryDBContext context = new PrimaryDBContext();
        return context.GetData().ToList();
    }
Run Code Online (Sandbox Code Playgroud)

我创建了一个快速控制台应用程序来测试,并在运行后添加了对 - System.Data.ServicesSystem.Data.Services.Clientthis 的引用Install-Package EntityFramework -Pre,但库上的版本是4.0而不是5.x.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Services.Client;
using ConsoleApplication1.PrimaryDBService;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            DataServiceContext context = new DataServiceContext(new Uri("http://localhost:50100/PrimaryDataService1.svc/"));
            IEnumerable<GetData_Result> result = context.Execute<GetData_Result>(new Uri("http://localhost:50100/PrimaryDataService1.svc/GetDataSproc"));
            foreach (GetData_Result w in result)
            {
                Console.WriteLine(w.ID + "\t" + w.WHO_TYPE_NAME + "\t" + w.CREATED_DATE);
            }

            Console.Read();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我没有使用UriKind.Relative或其他任何东西使这复杂化.

当我在浏览器中导航到URL时,我看到数据,但是当我在控制台应用程序中使用它时,我什么也得不到.

添加跟踪到混合:

  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true">
        <listeners>
          <add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="c:\temp\WebWCFDataService.svclog" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
Run Code Online (Sandbox Code Playgroud)

...并使用Microsoft服务跟踪查看器打开,我看到两个明显的警告:

找不到配置评估上下文.

<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
<System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
<EventID>524312</EventID>
<Type>3</Type>
<SubType Name="Warning">0</SubType>
<Level>4</Level>
<TimeCreated SystemTime="2012-04-03T14:50:11.8355955Z" />
<Source Name="System.ServiceModel" />
<Correlation ActivityID="{66f1a241-2613-43dd-be0c-341149e37d30}" />
<Execution ProcessName="WebDev.WebServer40" ProcessID="5176" ThreadID="10" />
<Channel />
<Computer>MyComputer</Computer>
</System>
<ApplicationData>
<TraceData>
<DataItem>
<TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Warning">
<TraceIdentifier>http://msdn.microsoft.com/en-US/library/System.ServiceModel.EvaluationContextNotFound.aspx</TraceIdentifier>
<Description>Configuration evaluation context not found.</Description>
<AppDomain>fd28c9cc-1-129779382115645955</AppDomain>
</TraceRecord>
</DataItem>
</TraceData>
</ApplicationData>
</E2ETraceEvent>
Run Code Online (Sandbox Code Playgroud)

那么为什么我能够从浏览器中看到数据,而不是在我的应用程序中使用时?

- 更新 -

我下载了2011微软WCF数据服务月CTP里面露出DataServiceProtocolVersion.V3,创造了一个新的主机和客户端和参考Microsoft.Data.Services.Client(v4.99.2.0).现在,在循环中尝试迭代时,在客户端上收到以下错误foreach:

客户端和服务之间存在类型不匹配.类型"ConsoleApplication1.WcfDataServiceOctCTP1.GetDataSproc_Result"是实体类型,但响应有效内容中的类型不表示实体类型.请确保客户端上定义的类型与服务的数据模型匹配,或更新客户端上的服务引用.

我通过引用实际实体尝试了同样的事情 - 工作得很好,同样的问题.

ElH*_*aix 2

回顾:我想创建一个返回强类型存储过程的高性能 WCF 服务 DAL(数据访问层)。我最初使用“WCF 数据服务”项目来完成此任务。它似乎有其局限性,在审查了不同 ORM 的性能指标后,我最终使用Dapper在基本 WCF 服务内进行数据访问。

\n\n

我首先创建了 *.edmx 模型并为我的存储过程创建了 POCO。

\n\n

接下来,我创建了一个基础 BaseRepository 和 MiscDataRepository:

\n\n
namespace WcfDataService.Repositories\n{\n    public abstract class BaseRepository\n    {\n        protected static void SetIdentity<T>(IDbConnection connection, Action<T> setId)\n        {\n            dynamic identity = connection.Query("SELECT @@IDENTITY AS Id").Single();\n            T newId = (T)identity.Id;\n            setId(newId);\n        }\n\n        protected static IDbConnection OpenConnection()\n        {\n            IDbConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["PrimaryDBConnectionString"].ConnectionString);\n            connection.Open();\n            return connection;\n        }\n    }\n}\n\nnamespace WcfDataService.Repositories\n{\n    public class MiscDataRepository : BaseRepository\n    {\n        public IEnumerable<GetData_Result> SelectAllData()\n        {\n            using (IDbConnection connection = OpenConnection())\n            {\n                var theData = connection.Query<GetData_Result>("sprocs_GetData",  \n                     commandType: CommandType.StoredProcedure);\n\n                return theData;\n            }\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

服务等级:

\n\n
namespace WcfDataService\n{\n    public class Service1 : IService1\n    {\n        private MiscDataRepository miscDataRepository;\n\n        public Service1()\n            : this(new MiscDataRepository())\n        {\n        }\n\n        public Service1(MiscDataRepository miscDataRepository)\n        {\n            this.miscDataRepository = miscDataRepository;\n        }\n\n        public IEnumerable<GetData_Result> GetData()\n        {\n            return miscDataRepository.SelectAllData();\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

...然后创建一个简单的控制台应用程序来显示数据:

\n\n
namespace ConsoleApplication1\n{\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            Service1Client client = new Service1Client();\n            IEnumerable<GetData_Result> result = client.GetData();\n            foreach (GetData_Result d in result)\n            {\n                Console.WriteLine(d.ID + "\\t" + d.WHO_TYPE_NAME + "\\t" + d.CREATED_DATE);\n            }\n            Console.Read();\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我还使用PetaPOCO完成了此任务,它的设置时间比 Dapper 少得多 - 只需几行代码:

\n\n
namespace PetaPocoWcfDataService\n{\n    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.\n    public class Service1 : IService1\n    {\n        public IEnumerable<GetData_Result> GetData()\n        {\n            var databaseContext = new PetaPoco.Database("PrimaryDBContext");  // using PetaPOCO for data access\n            databaseContext.EnableAutoSelect = false;                               // use the sproc to create the select statement\n\n            return databaseContext.Query<GetData_Result>("exec sproc_GetData");\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我喜欢设置 PetaPOCO 的快速和简单,但是将存储库模式与 Dapper 一起使用将更好地扩展企业项目。

\n\n

直接从 EDMX 创建复杂对象也非常简单 - 对于任何存储过程,然后使用它们。

\n\n

例如,我创建了ProfileDetailsByID_Result基于sq_mobile_profile_get_by_id存储过程调用的复杂类型返回类型。

\n\n
public ProfileDetailsByID_Result GetAllProfileDetailsByID(int profileID)\n{\n    using (IDbConnection connection = OpenConnection("DatabaseConnectionString"))\n    {\n        try\n        {\n            var profile = connection.Query<ProfileDetailsByID_Result>("sq_mobile_profile_get_by_id",\n                new { profileid = profileID },\n                commandType: CommandType.StoredProcedure).FirstOrDefault();\n\n            return profile;\n        }\n        catch (Exception ex)\n        {\n            ErrorLogging.Instance.Fatal(ex);        // use singleton for logging\n            return null;\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

因此,将 Dapper 与一些 EDMX 实体一起使用似乎是一种让事情顺利进行的快速方法。我可能是错的,但我不确定为什么 Microsoft 没有彻底考虑到这一点 - OData 不支持复杂类型。

\n\n

- - 更新 - -

\n\n

所以当我一个多月前提出这个问题时,我终于得到了微软的回复:

\n\n
\n

我们对此进行了研究,发现 Odata 客户端库不支持复杂类型。因此,我很遗憾地通知您,我们无能为力来解决这个问题。

\n\n

*可选:为了获得此问题的解决方案,您必须使用 Xml to Linq 类型的方法来获取复杂类型。

\n\n

非常感谢您对此事的理解。如果您有任何疑问,请告诉我。如果我们可以提供任何进一步的帮助,请告诉我们。

\n\n

此致,

\n
\n\n

看起来很奇怪。

\n