假ASMX Web服务调用

时间:2010-04-12 21:50:40

标签: c# .net xml web-services asmx

我构建了一个连接到SQL Server数据库的.NET ASMX Web服务。有一个Web服务调用GetAllQuestions()。

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

我将GetAllQuestions的结果保存到本地应用程序文件夹中的GetAllQuestions.xml

有没有办法伪造Web服务调用并使用本地xml结果?

我只想获取整个sql表的内容,并为我自动生成具有相关属性名称的对象数组,就像使用LINQ to SQL Web服务一样。

请记住,我正在构建一个独立的Monotouch iPhone应用程序。

4 个答案:

答案 0 :(得分:4)

使用dependency injection

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

或者,最好在对象的构造函数中设置SATService字段(因此构造函数需要设置SATService。如果这样做,它将更容易测试。

编辑抱歉,我会在这里详细说明。上面代码中的内容是耦合依赖,代码创建它正在使用的对象。依赖注入或控制反转(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();
    }
}

希望这会有所帮助。祝你好运!

答案 1 :(得分:0)

当你说假电话时,你只是测试客户端吗?

您可以使用fiddler,拦截请求并将本地xml文件返回给客户端。不要乱用你的客户代码。

答案 2 :(得分:0)

详细说明Audie的答案

使用DI可以得到你想要的东西。很简单,您将创建一个接口,您的真实对象和模拟对象都可以实现

public interface IFoo
{} 

然后你可以让你的GetSATService方法根据你的需要返回一个MockSATSerivce或真正的SATService对象。

这是您使用DI容器(某些对象存储具体类型映射的接口)的地方。您可以使用所需的类型来引导容器。因此,对于单元测试,您可以构造一个模拟容器,该容器将MockSATService注册为IFoo接口的实现者。

然后你将作为具体类型的容器但接口

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

然后在运行时,您只需更改容器,以便它使用运行时类型而不是模拟类型进行引导,但您的代码将保持不变(因为您将所有内容视为IFoo而不是SATService)

这有意义吗?

答案 3 :(得分: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();
        }
    }