1

我在 VS 2012 中开发了一个非常基本的 WCF 服务 (WCF 4.0)。我在 SQL Server 2012 中托管了 AdventureWorks2012 数据库,并且我正在使用安装了 .Net 4.0 框架的 IIS 7。

当我使用 VS 2012 在本地开发 IIS 中运行该服务时,一切正常(我假设因为托管 .svc 的网站的 web.config 中的连接字符串设置为使用集成安全性 - 连接字符串如下)

<add name="AdventureWorksEntities" connectionString="metadata=res://*/ProductsModel.csdl|res://*/ProductsModel.ssdl|res://*/ProductsModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=adams_pc;initial catalog=AdventureWorks2012;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient"/>

当我将网站发布到本地 IIS 并在 ASP.NET 4.0 应用程序池中运行时,似乎实际上没有与数据库建立连接(运行控制台测试客户端时,我的实体模型中的对象没有数据在他们中)。

我已经在 AdventureWorks2012 数据库中设置了名称为 IIS APPPOOL\ASP.NET v4.0 的用户,然后授予 dbo.owner 对数据库的访问权限,但我仍然无法建立连接。

我错过了什么?我需要更改托管网站的连接字符串吗?

更多信息:

当我在本地运行它时(通过开发服务器,我的意思是在我调试应用程序时运行的本地 IIS 实例)从数据库中检索信息并显示在测试控制台应用程序中。

当我部署服务并更改测试控制台应用程序中的引用以使用托管的 WCF 服务时,根本没有检索到任何数据。抛出的异常是我的测试代码中的空引用异常,但这是因为没有数据填充到我的服务使用的实体模型中。在数据库连接方面我没有看到任何异常,但我还没有到学习如何将异常从服务传递给客户端的地步。我期待这个简单的测试能像我正在使用的书一样工作。

此外,在建立数据库连接时,我还应该做些什么来准确找出错误是什么?

服务:

Imports System.Text
Imports System.Linq
Imports System
Imports System.Collections.Generic
Imports System.ServiceModel
Imports System.ServiceModel.Web
Imports System.Runtime.Serialization
Imports ProductsEntityModel
'NOTE: You can use the "Rename" command on the context menu to change the class name "Service" in code, svc and config file together.

'WCF service that implements the service contract
'This implementation performs minimal error checking and exception handling
Public Class ProductsServiceImpl
Implements IProductsService

Public Function ChangeStockLevel(productNumber As String, newStockLevel As Short, shelf As String, bin As Short) As Boolean Implements IProductsService.ChangeStockLevel
    'Modify the current stock level of the selected product in the ProductInventory table.
    'If the update is successful then return True, otherwise return False.

    'The Product and ProductIventory tables are joined over the ProductID column.

    Try
        'Connect to the AdventureWorks database using the Entity Framework
        Using database As New AdventureWorksEntities()
            ' Find the ProductID for the specified product
            Dim qryProductID = (From p In database.Products
                             Where String.Compare(p.ProductNumber, productNumber) = 0
                             Select p.ProductID).First
            'Find the ProductInventory object that matches the paramters passed into the operation
            Dim productInv As ProductInventory = database.ProductInventories.First(Function(pi) String.Compare(pi.Shelf, shelf) = 0 And pi.Bin = bin And pi.ProductID = qryProductID)

            'Update the stock level for the ProductInventory object
            productInv.Quantity += newStockLevel

            'Save the change back to the database
            database.SaveChanges()
        End Using
    Catch ex As Exception
        Return False
    End Try
    Return True
End Function


Public Function CurrentStockLevel(ByVal productNumber As String) As Integer Implements IProductsService.CurrentStockLevel
    'Obtain the total stock level for the specified product
    'The stock level is calculated by summing the quantity of the product available in all the bins in 
    'the ProductInventory table

    'The Product and ProductInventory tables are joined over the ProductID column.
    Dim stockLevel As Integer = 0

    Try
        'Connect to the AdventureWorks database by using the Entity Framework.
        Using database As New AdventureWorksEntities()
            stockLevel = (From pi In database.ProductInventories
                          Join p In database.Products
                          On pi.ProductID Equals p.ProductID
                          Where String.Compare(p.ProductNumber, productNumber) = 0
                          Select CInt(pi.Quantity)).Sum

        End Using
    Catch ex As Exception

    End Try
    Return stockLevel
End Function

Public Function GetProduct(productNumber As String) As ProductData Implements IProductsService.GetProduct
    'Create a reference to a ProductData object
    Dim productData As ProductData = Nothing

    Try
        'Connect to the AdventureWorks database by using Entity Framework
        Using database As New AdventureWorksEntities()
            Dim matchingProduct As Product = database.Products.First(Function(p) String.Compare(p.ProductNumber, productNumber) = 0)
            productData = New ProductData With {.Name = matchingProduct.Name, .ProductNumber = matchingProduct.ProductNumber, .Color = matchingProduct.Color, .ListPrice = matchingProduct.ListPrice}
        End Using
    Catch ex As Exception

    End Try
    Return productData
End Function

Public Function ListProducts() As List(Of String) Implements IProductsService.ListProducts
    'Create a list for holding product numbers
    Dim productsList As New List(Of String)

    Try
        'Connect to the AdventureWorks database by uysing the Entity Framework
        Using database As New AdventureWorksEntities()
            ' Fetch the product number of every product in the database
            Dim products = From Product In database.Products
                           Select Product.ProductNumber

            productsList = products.ToList
        End Using
    Catch ex As Exception

    End Try
    'Return the list of product numbers
    Return productsList
End Function
End Class

界面:

Imports System.Text
Imports System.Linq
Imports System
Imports System.Collections.Generic
Imports System.ServiceModel
Imports System.ServiceModel.Web
Imports System.Runtime.Serialization
'NOTE: You can use the "Rename" command on the context menu to change the interface name "IService" in both code and config file together.
'The Data Contact describes the details of a Product object passed to client applications (this is a return object).
<DataContract>
Public Class ProductData
<DataMember>
Public Name As String

<DataMember>
Public ProductNumber As String

<DataMember>
Public Color As String

<DataMember>
Public ListPrice As Decimal
End Class
'The Service Contact describes the operations that the WCF service provides (these are the methods available to call)
<ServiceContract>
Public Interface IProductsService

'Get the product number of every product
<OperationContract>
Function ListProducts() As List(Of String)

'Get the details of a single product
<OperationContract>
Function GetProduct(ByVal productNumber As String) As ProductData

'Get the current stock level for a product
<OperationContract>
Function CurrentStockLevel(ByVal productNumber As String) As Integer

'Change the stock level for a product
<OperationContract>
Function ChangeStockLevel(ByVal productNumber As String, ByVal newStockLevel As Short, ByVal shelf As String, ByVal bin As Int16) As Boolean

End Interface

客户端:

Imports System.ServiceModel
Imports ProductsClient.ProductsService

Module Module1

Sub Main()
    ' Create a proxy object and connect to the service
    Dim proxy As New ProductsServiceClient()

    ' Test the operations of the service

    ' Obtain a list of all the products
    Console.WriteLine("Test 1: List all products")
    Dim productNumbers As String() = proxy.ListProducts
    For Each productNumber As String In productNumbers
        Console.WriteLine("Number: {0}", productNumber)
    Next
    Console.WriteLine()

    Console.WriteLine("Test 2: Display the Details of a product")
    Dim product As ProductData = proxy.GetProduct("WB-H098")
    Console.WriteLine("Number: {0}", product.ProductNumber)
    Console.WriteLine("Name: {0}", product.Name)
    Console.WriteLine("Color: {0}", product.Color)
    Console.WriteLine("Price: {0}", product.ListPrice)
    Console.WriteLine()

    ' Query the stock level of this product
    Console.WriteLine("Test 3: Display the stock level of a product")
    Dim numInStock As Integer = proxy.CurrentStockLevel("WB-H098")
    Console.WriteLine("Number in stock: {0}", numInStock)
    Console.WriteLine()

    ' Modify the stock level of this product
    Console.WriteLine("Test 4: Modify the stock level of a product")
    If proxy.ChangeStockLevel("WB-H098", 100, "N/A", 0) Then
        numInStock = proxy.CurrentStockLevel("WB-H098")
        Console.WriteLine("Stock level changed. Current stock level: {0}", numInStock)
    Else
        Console.WriteLine("Stock level update failed.")
    End If
    Console.WriteLine()

    ' Disconnect from the service
    proxy.Close()
    Console.WriteLine("Press ENTER to finish")
    Console.ReadLine()
End Sub

End Module

只是为了咯咯笑,这是我用来在 AdventureWorks2012 数据库中设置我的用户的 SQL 脚本:

USE [AdventureWorks2012]
GO
CREATE USER [IIS APPPOOL\DefaultAppPool] FOR LOGIN [IIS APPPOOL\DefaultAppPool]
GO
EXEC sp_addrolemember N'db_owner', [IIS APPPOOL\DefaultAppPool]
GO
GO
CREATE USER [IIS APPPOOL\ASP.NET v4.0] FOR LOGIN [IIS APPPOOL\ASP.NET v4.0]
GO
EXEC sp_addrolemember N'db_owner', [IIS APPPOOL\ASP.NET v4.0]
GO

这是客户端 app.config 的 serviceModel 部分,因此您可以看到端点

<system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="BasicHttpBinding_IProductsService" />
        </basicHttpBinding>
    </bindings>
    <client>
        <endpoint address="http://localhost/ProductsService/Service.svc" binding="basicHttpBinding"
            bindingConfiguration="BasicHttpBinding_IProductsService" contract="ProductsService.IProductsService"
            name="BasicHttpBinding_IProductsService" />
    </client>
</system.serviceModel>
4

2 回答 2

2

您的服务代码会静默捕获所有异常。这样的代码是不可接受的:)。

要调试您的真实异常,请以管理员身份运行您的 Visual Studio。选择调试 -> 附加到进程 -> 选中“显示所有用户的进程” -> 选择“w3wp.exe”。运行您的测试应用程序并在 Visual Studio 中等待 Web 服务异常。

于 2012-06-10T19:42:24.563 回答
0

好吧,事实证明问题是我缺少应用程序池的服务器用户。我发布的 SQL 脚本只为应用程序池身份创建了一个数据库用户,但同一身份没有服务器登录。一旦我添加了该用户(并且我必须手动键入 IIS APPPOOL\ASP.NET v4.0,而不是尝试在 Windows 登录选择中选择它作为对象),一切正常。

感谢您对 Grzegorz 的帮助,尽管这最终完全是微不足道的事情!

于 2012-06-10T22:28:32.793 回答