2010-04-12 2 views
1

Я построил веб-службу .NET ASMX, подключившись к базе данных SQL Server. Существует вызов веб-службы GetAllQuestions().Поддельный вызов службы веб-службы ASMX

var myService = new SATService(); 
var serviceQuestions = myService.GetAllQuestions(); 

я сохранил результат GetAllQuestions к GetAllQuestions.xml в локальной папке приложения

Есть ли способ, чтобы подделать вызов службы веб и использовать локальный результат XML?

Я просто хочу взять содержимое всей моей таблицы sql и иметь массив объектов с корреляционными именами свойств, автоматически сгенерированными для меня, как с помощью веб-служб LINQ to SQL.

Пожалуйста, имейте в виду, что я создаю автономное приложение iPhone Monotouch.

+0

Можете ли вы добавить код, чтобы сохранить результат GetAllQuestions в GetAllQuestions.xml? – dtb

+0

Код отсутствует. Я физически спас его сам. – Bryan

ответ

4

Использование dependency injection.

//GetSATService returns the fake service during testing  
var myService = GetSATService(); 
var serviceQuestions = myService.GetAllQuestions(); 

или, предпочтительно, в конструкторе объекта, установленного в поле SATService (так что конструктор требует SATService должен быть установлен. Если вы сделаете это, то это будет легче проверить.

Редактировать : Извините, я подробно остановимся здесь. Что вы имеете в своем коде выше, это связанная зависимость, где ваш код создает объект, который он использует. Инъекционная инъекция или шаблон Inversion of Control (IOC), вы бы разобрали эту зависимость (Или просто не называйте «новым» - пусть что-то еще делает это - что-то, что вы можете контролировать вне потребителя.)

Есть несколько способов сделать это, и они показаны в приведенном ниже коде (комментарии поясните):

class Program 
{ 
    static void Main(string[] args) 
    { 
     //ACTUAL usage 
     //Setting up the interface injection 
     IInjectableFactory.StaticInjectable = new ConcreteInjectable(1); 

     //Injecting via the constructor 
     EverythingsInjected injected = 
      new EverythingsInjected(new ConcreteInjectable(100)); 

     //Injecting via the property 
     injected.PropertyInjected = new ConcreteInjectable(1000); 

     //using the injected items 
     injected.PrintInjectables(); 
     Console.WriteLine(); 

     //FOR TESTING (normally done in a unit testing framework) 
     IInjectableFactory.StaticInjectable = new TestInjectable(); 
     EverythingsInjected testInjected = 
      new EverythingsInjected(new TestInjectable()); 
     testInjected.PropertyInjected = new TestInjectable(); 
     //this would be an assert of some kind 
     testInjected.PrintInjectables(); 

     Console.Read(); 
    } 

    //the inteface you want to represent the decoupled class 
    public interface IInjectable { void DoSomething(string myStr); } 

    //the "real" injectable 
    public class ConcreteInjectable : IInjectable 
    { 
     private int _myId; 
     public ConcreteInjectable(int myId) { _myId = myId; } 
     public void DoSomething(string myStr) 
     { 
      Console.WriteLine("Id:{0} Data:{1}", _myId, myStr); 
     } 
    } 

    //the place to get the IInjectable (not in consuming class) 
    public static class IInjectableFactory 
    { 
     public static IInjectable StaticInjectable { get; set; } 
    } 

    //the consuming class - with three types of injection used 
    public class EverythingsInjected 
    { 
     private IInjectable _interfaceInjected; 
     private IInjectable _constructorInjected; 
     private IInjectable _propertyInjected; 

     //property allows the setting of a different injectable 
     public IInjectable PropertyInjected 
     { 
      get { return _propertyInjected; } 
      set { _propertyInjected = value; } 
     } 

     //constructor requires the loosely coupled injectable 
     public EverythingsInjected(IInjectable constructorInjected) 
     { 
      //have to set the default with property injected 
      _propertyInjected = GetIInjectable(); 

      //retain the constructor injected injectable 
      _constructorInjected = constructorInjected; 

      //using basic interface injection 
      _interfaceInjected = GetIInjectable(); 
     } 

     //retrieves the loosely coupled injectable 
     private IInjectable GetIInjectable() 
     { 
      return IInjectableFactory.StaticInjectable; 
     } 

     //method that consumes the injectables 
     public void PrintInjectables() 
     { 
      _interfaceInjected.DoSomething("Interface Injected"); 
      _constructorInjected.DoSomething("Constructor Injected"); 
      _propertyInjected.DoSomething("PropertyInjected"); 
     } 
    } 

    //the "fake" injectable 
    public class TestInjectable : IInjectable 
    { 
     public void DoSomething(string myStr) 
     { 
      Console.WriteLine("Id:{0} Data:{1}", -10000, myStr + " For TEST"); 
     } 
    } 

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

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

class Program 
{ 
    static void Main(string[] args) 
    { 
     ServiceFactory factory = new ServiceFactory(false); 
     //first call hits the webservice 
     GetServiceQuestions(factory); 
     //hists the cache next time 
     GetServiceQuestions(factory); 
     //can refresh on demand 
     factory.ResetCache = true; 
     GetServiceQuestions(factory); 
     Console.Read(); 
    } 

    //where the call to the "service" happens 
    private static List<Question> GetServiceQuestions(ServiceFactory factory) 
    { 
     var myFirstService = factory.GetSATService(); 
     var firstServiceQuestions = myFirstService.GetAllQuestions(); 
     foreach (Question question in firstServiceQuestions) 
     { 
      Console.WriteLine(question.Text); 
     } 
     return firstServiceQuestions; 
    } 
} 

//this stands in place of your xml file 
public static class DataStore 
{ 
    public static List<Question> Questions; 
} 

//a simple question 
public struct Question 
{ 
    private string _text; 
    public string Text { get { return _text; } } 
    public Question(string text) 
    { 
     _text = text; 
    } 
} 

//the contract for the real and fake "service" 
public interface ISATService 
{ 
    List<Question> GetAllQuestions(); 
} 

//hits the webservice and refreshes the store 
public class ServiceWrapper : ISATService 
{ 
    public List<Question> GetAllQuestions() 
    { 
     Console.WriteLine("From WebService"); 
     //this would be your webservice call 
     DataStore.Questions = new List<Question>() 
        { 
         new Question("How do you do?"), 
         new Question("How is the weather?") 
        }; 
     //always return from your local datastore 
     return DataStore.Questions; 
    } 
} 

//accesses the data store for the questions 
public class FakeService : ISATService 
{ 
    public List<Question> GetAllQuestions() 
    { 
     Console.WriteLine("From Fake Service (cache):"); 
     return DataStore.Questions; 
    } 
} 

//The object that decides on using the cache or not 
public class ServiceFactory 
{ 
    public bool ResetCache{ get; set;} 
    public ServiceFactory(bool resetCache) 
    { 
     ResetCache = resetCache; 
    } 
    public ISATService GetSATService() 
    { 
     if (DataStore.Questions == null || ResetCache) 
      return new ServiceWrapper(); 
     else 
      return new FakeService(); 
    } 
} 

Надеюсь, что это поможет. Удачи!

+0

Звучит здорово, но можете ли вы быть более конкретным? Я просмотрел страницу wiki и не смог найти код C#, который мне нужен. Что такое GetSATService(); – Bryan

+0

супер простой способ: GetSatService - это защищенный виртуальный метод, который по умолчанию возвращает вашу службу. В вашем тесте вы можете переопределить GetSatService, чтобы вернуть свою собственную реализацию. Или вы можете передать свою реализацию через свой конструктор классов. – Juliet

+0

@ Bryan, я подробно описал выше, с полной рабочей программой, которая показывает различные типы инъекций. Задайте здесь любые вопросы, и я постараюсь помочь. В основном, просто выберите тип инъекции, который легко для вашей конкретной ситуации, и вы сможете легко протестировать. – Audie

0

, когда вы говорите, поддельный звонок, вы просто проверяете клиентскую сторону?

вы можете использовать скрипач, перехватить запрос и вернуть локальный файл xml клиенту. Тогда не возиться со своим кодом клиента.

+0

Как бы мне сделать этот Стивен? Что такое скрипач? – Bryan

+0

fiddler - http proxy/debugger - http://www.fiddler2.com/fiddler2/ Установите и запустите. Весь ваш трафик http будет указан. Затем создайте автоответчик, введите Url веб-службы и выберите локальный файл, который вы хотели бы вернуть fiddler. Fiddler перехватит запрос на webserevice и вместо этого вернет локальный XML-файл. –

+0

Похоже, что это будет работать чудесно в веб-ситуации, но я создаю приложение для iPhone, использующее монотот. – Bryan

0

Чтобы уточнить ответ Оди в

Использование DI бы получить, что вы хотите. Очень просто вы можете создать интерфейс, который ваш реальный объект и ваш фиктивный объект как реализовать

public interface IFoo 
{} 

Тогда вы бы иметь свой метод GetSATService возвращение либо MockSATSerivce или реальный объект SATService в зависимости от потребностей.

Здесь вы должны использовать контейнер DI (некоторый объект, который хранит интерфейс для сопоставлений конкретного типа). Вы загрузите контейнер с типами, которые вы хотите. Итак, для единичного теста вы можете создать макетный контейнер, который регистрирует MockSATService как исполнитель интерфейса IFoo.

Тогда вы бы в качестве контейнера для конкретного типа, но интерфейс

IFoo mySATService = Container.Resolve<IFoo>(); 

Тогда во время выполнения вы просто изменить из контейнера таким образом, что он бутстрэпами с типами среды выполнения вместо фиктивных типов, но вы код будет оставайтесь неизменными (потому что вы лечите все как IFoo вместо SATService)

Это имеет смысл?

+0

Будет ли это означать, что я должен объявить класс и типы IFoo? Причина, по которой я хочу использовать объект веб-службы, заключается в том, что типы и имена уже создаются автоматически на основе моей базы данных sql. – Bryan

+0

Да, вам нужно объявить интерфейс и конкретные типы. Прокси-класс auto gen не поймет, как это сделать для вас. Честно говоря, я думаю, что вы слишком сильно склоняетесь к классам автогенераторов для того, что вы пытаетесь сделать. У вас должен быть объект поставщика GetAllQuestions, который понимает, как переключаться между WebService и локальным хранилищем файлов в качестве хранилища данных резервного копирования. Здесь DI светит. Вы можете без проблем реализовывать реализацию общего интерфейса. –

0

Со временем я обнаружил, что интересный способ сделать это - извлечь интерфейс и создать класс-оболочку. Это хорошо адаптируется к контейнеру IoC, а также отлично работает без него.

При тестировании создайте класс, проходящий поддельную службу. При обычном использовании просто вызовите пустой конструктор, который может просто создать поставщика или разрешить его с помощью файла конфигурации.

public DataService : IDataService 
    { 
     private IDataService _provider; 

     public DataService() 
     { 
      _provider = new RealService(); 
     } 

     public DataService(IDataService provider) 
     { 
      _provider = provider; 
     } 

     public object GetAllQuestions() 
     { 
      return _provider.GetAllQuestions(); 
     } 
    } 
Смежные вопросы