Создание операции службы данных WCF с объектами в памяти

Я пытаюсь создать WCF DataService, используя граф объектов в памяти. Это означает, что серверная часть — это не хранилище Entity Framework, а набор объектов, находящихся в памяти.

Я пытаюсь создать операцию службы с именем GetUsersByName, которая имеет один параметр для имени и возвращает соответствующих пользователей в виде коллекции IQueryable.

Я следовал документации и добавил правила доступа для этой операции

config.SetServiceOperationAccessRule("GetUsersByName", ServiceOperationRights.All);

Но когда вызывается метод SetServiceOperationAccessRule, я получаю исключение на клиенте:

System.AggregateException was unhandled.

Вот полный код моего консольного приложения

    using System;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    using System.ServiceModel.Description;
    using System.Data.Services;
    using System.Data.Services.Common;
    using System.Collections.Generic;
    using System.Runtime.Serialization;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Web;
    using System.Net.Http;
    using System.Net;
    using System.IO; 

    namespace WCF_OData
    {

        class Program
        {
            static void Main(string[] args)
            {
                string serviceAddress = "http://localhost:8080";
                Uri[] uriArray = { new Uri(serviceAddress) };
                Type serviceType = typeof(UserDataService);

                using (var host = new DataServiceHost(serviceType, uriArray)) {
                    host.Open();

                    var client = new HttpClient() { BaseAddress = new Uri(serviceAddress) };
                    Console.WriteLine("Client received: {0}", client.GetStringAsync("Users?$format=json").Result);

                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:8080");
                    request.Method = "GET";
                    request.Accept = @"application/json";

                    using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                    {
                        Console.WriteLine(response.StatusCode);
                        Console.WriteLine(response.ContentType);
                        Console.WriteLine((new StreamReader(response.GetResponseStream())).ReadToEnd());
                    }

                    Console.WriteLine("Press any key to stop service");
                    Console.ReadKey();
                }

            }
        }

        [EnableJsonSupport]
        [ServiceBehavior(IncludeExceptionDetailInFaults = true)]   
        public class UserDataService : DataService<UserService> {
            public static void InitializeService(DataServiceConfiguration config)
            {
                config.SetEntitySetAccessRule("Users", EntitySetRights.All);
                config.SetServiceOperationAccessRule("GetUsersByName", ServiceOperationRights.All);

                config.UseVerboseErrors = true;

                config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
            }

        }

        public class UserService
        {
            private List<User> _List = new List<User>();

            public UserService()
            {
                _List.Add(new User() { ID = 1, UserName = "John Doe" });
                _List.Add(new User() { ID = 2, UserName = "Jane Doe" });
            }

            public IQueryable<User> Users
            {
                get
                {
                    HttpContext x = HttpContext.Current;
                    return _List.AsQueryable<User>();
                }
            }

            [OperationContract]
            [WebGet(UriTemplate="GetUsersByName")]
            public IQueryable<User> GetUsersByName(string name)
            {
                return new List<User>().AsQueryable(); 
            }
        }

        [DataServiceKey("ID")]
        public class User
        {
            public int ID { get; set; } 
            public string UserName { get; set; }
        }
    }

person gyurisc    schedule 24.08.2012    source источник
comment
Может быть, я что-то упускаю, но почему ваш сервис и консольное приложение находятся в одном месте? Как вы размещаете сервис? Вы пытались вызвать сервисную операцию из браузера? Что говорит трассировка стека?   -  person Mark Stafford - MSFT    schedule 24.08.2012
comment
Я хочу, чтобы это был самостоятельный сервис. Я размещаю службу в консольном проекте, а затем вызываю службу из того же приложения. Вызовы находятся в консоли только для облегчения тестирования.   -  person gyurisc    schedule 29.08.2012


Ответы (1)


Похоже, что здесь происходит несколько вещей, поэтому для их обработки может потребоваться несколько итераций. Первая проблема, которую следует исправить, — это работа службы. Операции службы должны быть объявлены в классе, который наследуется от DataService: "Операции службы — это методы добавлен в класс службы данных, производный от DataService". Вот пример:

using System.Data.Entity;
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<ScratchEntityFrameworkContext>
    {
        static ScratchService()
        {
            Database.SetInitializer(new ScratchEntityFrameworkContextInitializer());
        }

        public static void InitializeService(DataServiceConfiguration config)
        {
            config.SetEntitySetAccessRule("*", EntitySetRights.All);
            config.SetServiceOperationAccessRule("*", ServiceOperationRights.AllRead);
            config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
            config.UseVerboseErrors = true;
        }

        [WebGet]
        public IQueryable<Product> FuzzySearch(string idStartsWith)
        {
            var context = new ScratchEntityFrameworkContext();
            return context.Products.ToList().Where(p => p.ID.ToString().StartsWith(idStartsWith)).AsQueryable();
        }
    }
}

После этого вы сможете вызывать операцию службы из браузера с форматом URL, подобным следующему: http://localhost:59803/ScratchService.svc/FuzzySearch()?idStartsWith='1'

Можем ли мы начать с того, что попытаемся получить этот функционал в браузере, а затем посмотреть, возникает ли все еще AggregateException?

person Mark Stafford - MSFT    schedule 24.08.2012
comment
это так работает. Единственная проблема, которая у меня есть сейчас, заключается в том, что я не могу автоматически создать прокси-сервер в своих клиентах (путем добавления веб-ссылки), который также включал бы операции. Насколько я понимаю, это не реализовано. - person gyurisc; 30.08.2012
comment
Я считаю, что это правильно - нет генерации кода для сервисных операций (пока :)). - person Mark Stafford - MSFT; 30.08.2012