WCF Data Services 5.0 Обходной путь для возврата POCO?

Я почти уверен, что это не так, но извиняюсь, если этот вопрос уже был задан. И дополнительные извинения, если это просто глупый вопрос, но я чувствую, что либо полностью что-то упускаю, либо имею правильную идею, и мне просто нужна поддержка для моего собственного здравомыслия.

Я внедряю WCF Data Services 5.0 в наше приложение, и у меня нет проблем с операциями чтения, возвращающими объекты сущностей.

К сожалению, когда речь идет о сервисных операциях, существует неприятное ограничение, заключающееся в том, что они могут возвращать только примитивные типы (см. MSDN). Это очень раздражает, учитывая, что у него нет проблем с объектными объектами.

Я знаю, что одним из обходных путей является создание «фиктивного» сложного типа, поскольку WCFDS распознает это, но я не хочу просто добавлять в свою модель данных случайные POCO, которых на самом деле нет в базе данных.

Поэтому решение, которое пришло мне в голову, состояло в том, чтобы создать метод расширения для моих объектов, который может сериализовать их в строки JSON, которые будут возвращены службой. Мой вопрос: есть ли веские аргументы, почему мне не следует этого делать, или кто-нибудь может предложить лучшие альтернативы?


Изменить: дополнительная информация для разъяснения моих текущих проблем

Я создал очень простой пример того, что я делаю, который изначально поднял этот вопрос. Мой класс обслуживания следует первым:

[JsonpSupportBehavior]
public partial class SchedulingService : DataService<ChronosDataContext>, ISchedulingService
{
    public static void InitializeService(DataServiceConfiguration config)
    {
        #if DEBUG
        config.UseVerboseErrors = true;
        #endif

        config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;

        config.SetServiceOperationAccessRule(
        "TestService",
        ServiceOperationRights.All);
    }

    [WebGet]
    public SchedulingResult TestService(
         string testParam1,
         string testParam2)
    {
         // NOTE: I never use the params, they're just there for this example.
         SchedulingResult result = SchedulingResult.Empty;

         result.Status = OperationStatus.Success;
         result.ResponseID = Guid.NewGuid();
         result.AffectedIDs = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7 });
         result.RecordsAffected = 10;

         return result;
    }
}

При попытке получить доступ к этой службе с помощью моего браузера я получаю следующую ошибку запроса:

The server encountered an error processing the request. The exception message is 
'Unable to load metadata for return type 
    'Chronos.Services.SchedulingResult' of method 
    'Chronos.Services.SchedulingResult TestService(System.String, System.String)'.'. 
See server logs for more details. 

The exception stack trace is:
at System.Data.Services.Providers.BaseServiceProvider.AddServiceOperation(MethodInfo method, String protocolMethod) 
at System.Data.Services.Providers.BaseServiceProvider.AddOperationsFromType(Type type) 
at System.Data.Services.Providers.BaseServiceProvider.LoadMetadata() 
at System.Data.Services.DataService`1.CreateMetadataAndQueryProviders(IDataServiceMetadataProvider& metadataProviderInstance, IDataServiceQueryProvider& queryProviderInstance, BaseServiceProvider& builtInProvider, Object& dataSourceInstance) 
at System.Data.Services.DataService`1.CreateProvider() 
at System.Data.Services.DataService`1.HandleRequest() 
at System.Data.Services.DataService`1.ProcessRequestForMessage(Stream messageBody) 
at SyncInvokeProcessRequestForMessage(Object , Object[] , Object[] ) 
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs) 
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc) 
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)

Ниже приведены классы, составляющие SchedulingResult, который я пытаюсь вернуть:

public class SchedulingResult : ServiceInvocationResponse
{
    public SchedulingResult()
        : base()
    {
        this.Payload = new object[] 
        {
            new List<int>(),
            new List<int>()
        };
    }

    public List<int> AffectedIDs 
    {
        get { return (List<int>)Payload[0]; }
        set { Payload[0] = value; }
    }

    public List<int> FailedIDs
    {
        get { return (List<int>)Payload[1]; }
        set { Payload[1] = value; }
    }

    public static SchedulingResult Empty
    {
        get { return new SchedulingResult(); }
    }
}

public class ServiceInvocationResponse : AbstractJsonObject<ServiceInvocationResponse>
{
    public ServiceInvocationResponse()
    {
        this.Status = OperationStatus.Unknown;
        this.Severity = ErrorSeverity.None;
    }

    public virtual int RecordsAffected { get; set; }

    public virtual Exception ErrorObject { get; set; }

    internal virtual object[] Payload { get; set; }
}


public abstract class AbstractJsonObject<TBaseType>
{
    public virtual object Deserialize(string source)
    {
        return JsonConvert.DeserializeObject(source);
    }

    public virtual T Deserialize<T>(string source)
    {
        return JsonConvert.DeserializeObject<T>(source);
    }

    public string Serialize()
    {
        return JsonConvert.SerializeObject(
            this, Formatting.Indented);
    }

    public override string ToString()
    {
        return this.Serialize();
    }

    public static TBaseType FromString(string json)
    {
        return JsonConvert.DeserializeObject<TBaseType>(json);
    }
}

person Jason    schedule 10.07.2012    source источник
comment
Возможно, я что-то упускаю, но почему вы думаете, что сервисные операции могут возвращать только примитивные типы? Согласно приведенной вами статье, они могут возвращать void, тип объекта/комплекса/примитива или коллекцию (IEnumerable или IQueryable) типов объекта/комплекса/примитива. См. также: msdn.microsoft.com/en-us/ библиотека/hh230677(v=vs.103)   -  person Mark Stafford - MSFT    schedule 12.07.2012
comment
Извините, я наполовину ошибся... Мне не удалось использовать один POCO в качестве возвращаемого типа, но, повторно изучив статью, я считаю, что она говорит мне, что я могу вернуть коллекцию (IEnumerable/IQueryable) любого (сериализуемый) тип? Моя другая ОГРОМНАЯ проблема сейчас заключается в том, что сериализация объектов, которые я использую в качестве параметров, в строки делает запрос слишком длинным... но я отправлю отдельный вопрос для этого, поскольку это отдельная проблема.   -  person Jason    schedule 12.07.2012


Ответы (1)


Из операции службы можно вернуть один или несколько примитивных, сложных или сущностных типов.

  • Примитивный тип — это то, что вы ожидаете: string, int, bool и т. д.
  • Сложный тип — это класс, у которого нет уникального ключа (свойство с именем ID или атрибут [DataServiceKey("<yourkeyhere>")]).
  • Тип сущности — это класс, который имеет уникальный ключ.

Например:

using System.Data.Services;
using System.Data.Services.Common;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace Scratch.Web
{
    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    public class ScratchService : DataService<ScratchContext>
    {
        public static void InitializeService(DataServiceConfiguration config)
        {
            config.SetEntitySetAccessRule("*", EntitySetRights.All);
            config.SetServiceOperationAccessRule("*", ServiceOperationRights.AllRead);
            config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
            config.UseVerboseErrors = true;
        }

        [WebGet]
        public string GetPrimitive()
        {
            return "Success";
        }

        [WebGet]
        public IQueryable<string> GetPrimitives()
        {
            return new[] { "Success", "Hello World" }.AsQueryable();
        }

        [WebGet]
        public ComplexType GetComplexType()
        {
            return new ComplexType { Property1 = "Success", Property2 = "Hello World" };
        }

        [WebGet]
        public IQueryable<ComplexType> GetComplexTypes()
        {
            return new[] {
                           new ComplexType { Property1 = "Success", Property2 = "Hello World" },
                           new ComplexType { Property1 = "Success", Property2 = "Hello World" }
                       }.AsQueryable();
        }

        [WebGet]
        public EntityType GetEntityType()
        {
            return new EntityType { Property1 = "Success", Property2 = "Hello World" };
        }

        [WebGet]
        public IQueryable<EntityType> GetEntityTypes()
        {
            return new[] {
                           new EntityType { Property1 = "Success1", Property2 = "Hello World" },
                           new EntityType { Property1 = "Success2", Property2 = "Hello World" }
                       }.AsQueryable();
        }
    }

    public class ScratchContext { }

    public class ComplexType
    {
        public string Property1 { get; set; }
        public string Property2 { get; set; }
    }

    [DataServiceKey("Property1")]
    public class EntityType
    {
        public string Property1 { get; set; }
        public string Property2 { get; set; }
    }
}

Возможно, вы столкнулись с какой-то другой проблемой?

person Mark Stafford - MSFT    schedule 12.07.2012
comment
Привет, Марк, я обновил свой вопрос, добавив код, с которым у меня возникли проблемы. Пожалуйста, дайте мне знать, если у вас есть какие-либо предложения, спасибо! - person Jason; 13.07.2012
comment
Пара вещей: - Сложные типы не поддерживают наследование, поэтому у вас не может быть SchedulingResult : ServiceInvocationResponse - Перечисления еще не являются частью OData, но вы можете обойти это с помощью ToString и Enum.Parse (и ваше свойство Status, похоже, не объявлен в цепочке наследования, но я предполагаю, что это была проблема копирования/вставки) — работает следующий слегка измененный пример: gist .github.com/3101195 - person Mark Stafford - MSFT; 13.07.2012
comment
@mark-stafford-msft Вы, сэр... у вас подарок. Удаление наследования и перечислений сделало свое дело. Спасибо вам так так так так много! - person Jason; 13.07.2012
comment
Есть ли противоречие с stackoverflow.com/questions/14525606/? Какой провайдер здесь используется? У меня не работает с поставщиком EF («Невозможно загрузить метаданные для возвращаемого типа «DSDBService.ComplexType» метода «DSDBService.ComplexType GetComplexType()») - person dgorissen; 17.06.2013