Как использовать сложный объект из sproc с помощью WCF Data Services / OData? - PullRequest
5 голосов
/ 03 апреля 2012

Используя WCF Data Services (и последнюю версию Entity Framework), я хочу вернуть данные из хранимой процедуры. Возвращенные 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();
    }

Я создал быстрое консольное приложение для тестирования и добавил ссылку на System.Data.Services и System.Data.Services.Client - это после запуска 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();
        }
    }
}

Я не использовал 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>

... и открываясь с помощью Microsoft Service Trace Viewer, я вижу два идентичных предупреждения:

Контекст оценки конфигурации не найден.

<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>

Так почему я могу видеть данные из браузера, но не когда они используются в моем приложении?

- ОБНОВЛЕНИЕ -

Я загрузил Microsoft WCF Data Services, октябрь 2011 г., CTP , в котором было указано DataServiceProtocolVersion.V3, создан новый хост и клиент, а также ссылка на Microsoft.Data.Services.Client (v4.99.2.0). Теперь при попытке выполнить итерацию в цикле foreach на клиенте появляется следующая ошибка:

Существует несоответствие типов между клиентом и службой. Тип ConsoleApplication1.WcfDataServiceOctCTP1.GetDataSproc_Result является тип сущности, но тип в полезной нагрузке ответа не представляет тип объекта. Пожалуйста, убедитесь, что типы, определенные на клиенте, соответствуют модель данных службы или обновить ссылку на службу на клиент.

Я пытался сделать то же самое, ссылаясь на фактическую сущность - работает нормально, так же проблема.

1 Ответ

2 голосов
/ 05 апреля 2012

Напомним: я хочу создать высокопроизводительный DAL службы WCF (уровень доступа к данным), который возвращает строго типизированные хранимые процедуры.Сначала я использовал проект «Службы данных WCF» для достижения этой цели.Кажется, что у него есть свои ограничения, и после просмотра показателей производительности различных ORM я в конечном итоге использовал Dapper для доступа к данным внутри базовой службы WCF.

Сначала я создал модель * .edmx и создал POCO для моего sproc.

Затем я создал базовый BaseRepository и MiscDataRepository:

namespace WcfDataService.Repositories
{
    public abstract class BaseRepository
    {
        protected static void SetIdentity<T>(IDbConnection connection, Action<T> setId)
        {
            dynamic identity = connection.Query("SELECT @@IDENTITY AS Id").Single();
            T newId = (T)identity.Id;
            setId(newId);
        }

        protected static IDbConnection OpenConnection()
        {
            IDbConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["PrimaryDBConnectionString"].ConnectionString);
            connection.Open();
            return connection;
        }
    }
}

namespace WcfDataService.Repositories
{
    public class MiscDataRepository : BaseRepository
    {
        public IEnumerable<GetData_Result> SelectAllData()
        {
            using (IDbConnection connection = OpenConnection())
            {
                var theData = connection.Query<GetData_Result>("sprocs_GetData",  
                     commandType: CommandType.StoredProcedure);

                return theData;
            }
        }
    }
}

Класс обслуживания:

namespace WcfDataService
{
    public class Service1 : IService1
    {
        private MiscDataRepository miscDataRepository;

        public Service1()
            : this(new MiscDataRepository())
        {
        }

        public Service1(MiscDataRepository miscDataRepository)
        {
            this.miscDataRepository = miscDataRepository;
        }

        public IEnumerable<GetData_Result> GetData()
        {
            return miscDataRepository.SelectAllData();
        }
    }
}

... и затем создал простое консольное приложение для отображения данных:

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

Я также выполнил это с помощью PetaPOCO , что заняло гораздо меньше времени для настройки, чемDapper - несколько строк кода:

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

            return databaseContext.Query<GetData_Result>("exec sproc_GetData");
        }
    }
}

Мне нравится, как быстро и просто настроить PetaPOCO, но использование шаблона репозитория с Dapper будет гораздо лучше масштабироваться для корпоративного проекта.

Также было довольно просто создавать сложные объекты непосредственно из EDMX - для любой хранимой процедуры, а затем использовать их.

Например, я создал сложный тип возвращаемого типа с именем ProfileDetailsByID_Result на основе sq_mobile_profile_get_by_id sproc.

public ProfileDetailsByID_Result GetAllProfileDetailsByID(int profileID)
{
    using (IDbConnection connection = OpenConnection("DatabaseConnectionString"))
    {
        try
        {
            var profile = connection.Query<ProfileDetailsByID_Result>("sq_mobile_profile_get_by_id",
                new { profileid = profileID },
                commandType: CommandType.StoredProcedure).FirstOrDefault();

            return profile;
        }
        catch (Exception ex)
        {
            ErrorLogging.Instance.Fatal(ex);        // use singleton for logging
            return null;
        }
    }
}

Так что использование Dapper вместе с некоторыми объектами EDMX выглядит довольно быстроспособ добиться цели.Возможно, я ошибаюсь, но я не уверен, почему Microsoft не думала об этом все время - нет поддержки сложных типов с OData.

--- UPDATE ---

Итак, я наконец получил ответ от Microsoft, когда более месяца назад поднял вопрос:

Мы провели исследование по этому вопросу и обнаружили, что клиентская библиотека Odata неПоддерживает сложные типы.Поэтому я с сожалением сообщаю вам, что мы мало что можем сделать, чтобы решить ее.

* Необязательно: Чтобы получить решение этой проблемы, вы должны использовать подход типа Xml to Linqчтобы получить сложные типы.

Большое спасибо за ваше понимание в этом вопросе.Пожалуйста, дай мне знать, если возникнут какие-либо вопросы.Если нам понадобится дополнительная помощь, пожалуйста, сообщите нам.

С уважением,

Кажется странным.

...