记录

时间:2016-09-12 15:21:59

标签: c# wcf logging soap

我有一组WCF SOAP Web服务。一些服务操作允许用户提交数据。我想将这些请求的有效负载记录到SQL Server数据库。对于只读取数据的客户,我只想记录简单的元数据。

这是我的数据模型: enter image description here

我有一个实现IParameterInspector接口的类。所有服务操作请求都记录在BeforeCall方法中:请求时间,操作名称,用户ID和目标端点。

public class LogParameterInspector : IParameterInspector
{
    private static WebServiceLog WebServiceLog = WebServiceLog.GetInstance();
    private bool IsOperationIgnorable(string operationName)
    {
        //Ignore GETs for service metadata
        return string.Equals(operationName, "get", StringComparison.InvariantCultureIgnoreCase);
    }

    public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
    {
        if (IsOperationIgnorable(operationName))
            return;

        SsnWebServiceContext.Current.Response.CreatedDate = DateTime.Now;

        //Calls stored procedure
        WebServiceLog.LogServerResponseToDb(SsnWebServiceContext.Current.Response);
    }

    public object BeforeCall(string operationName, object[] inputs)
    {
        if (IsOperationIgnorable(operationName))
            return null;

        SsnWebServiceContext.Current.Request.CreatedDate = DateTime.Now;
        SsnWebServiceContext.Current.Request.OperationName = operationName;

        foreach (var item in inputs)
        {
            if (item == null) continue;

            if (item.GetType().GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IRequest<>)))
            {
                var header = item
                    .GetType()
                    .GetProperties()
                    .Single(p => p.Name == "Header")
                    .GetValue(item, null) as IRequestHeader;

                SsnWebServiceContext.Current.Request.UserIdentification = header?.UserName;
            }
        }

        //Calls stored procedure
        WebServiceLog.LogClientRequestToDb(SsnWebServiceContext.Current.Request);

        return null;
    }


}

消息有效负载显式记录在需要它的少数服务操作中。 (调用与BeforeCall中相同的存储过程。该过程检查具有给定GUID的记录是否已存在,如果存在,则更新它。)

我有一个实现IExtension&lt; OperationContext&gt;的类。该类使我能够在请求的生命周期内访问请求/响应数据。

public class SsnWebServiceContext : IExtension<OperationContext>
{
    //Helper property for syntax purposes
    public static SsnWebServiceContext Current => OperationContext.Current.Extensions.Find<SsnWebServiceContext>();

    //Called when extension is added to OperationContext. Useful for initializing stuff.
    public void Attach(OperationContext owner)
    {
        var tmp = Guid.NewGuid();

        Request = Request ?? new ClientRequest();
        Request.SetReadOnlyGuid(tmp);

        Response = Response ?? new ServerResponse();
        Response.SetReadOnlyGuid(tmp);
    }

    //Called when extension is removed from OperationContext. Use for for cleanup.
    public void Detach(OperationContext owner)
    {
    }

    public ClientRequest Request { get; set; }
    public ServerResponse Response { get; set; }
}

我的自定义上下文通过实现IDispatchMessageInspector的类添加到OperationContext。这也是我创建消息流副本以供将来参考的地方。

public class ContextDispatchMessageInspector : IDispatchMessageInspector
{
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {

        using (var buffer = request.CreateBufferedCopy(int.MaxValue))
        {
            OperationContext.Current.Extensions.Add(new SsnWebServiceContext
            {
                Request = new ClientRequest
                {
                    Message = buffer.CreateMessage(),
                    TargetRole = OperationContext.Current.IncomingMessageHeaders?.To?.ToString()
                }
            });

            request = buffer.CreateMessage();
        }

        return null; //Correlation state passed to BeforeSendReply
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        OperationContext.Current.Extensions.Remove(SsnWebServiceContext.Current);
    }
}

使用以下设置为basicHttpBinding配置了所有端点:

<bindings>
  <basicHttpBinding>
    <binding name="SSNBinding" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" >
      <security mode="None"></security>
    </binding>
  </basicHttpBinding>
</bindings>

问题:

除了较大的消息,日志记录工作正常。当我尝试查看SSMS中的一些XML消息时,出现以下错误:

enter image description here

如果我将XML内容复制到文本编辑器中,则表明尚未记录整个消息。我发现这只发生在用户上传文件大于2MB的请求时。文件作为base64编码的字符串上传,记录的XML内容在编码字符串中间的某处被截断。

奇怪(对我而言)该文件似乎已成功传输,即使它尚未完全记录。我使用SOAP UI和base64编码大图像在本地重现了这种行为:请求花了一段时间(在SOAP UI中1分钟后超时),但是仍然在一段时间后成功完成;我下载并查看了图像,但没有损坏。我用一个大的Word文档得到了相同的结果。

对于我可以做些什么来防止我的日志被截断的任何建议都将受到高度赞赏!

1 个答案:

答案 0 :(得分:1)

您的问题不是表中的数据被截断,而是SSMS限制了输出中的数据大小。所以不要担心丢失数据。你的代码很好。这就是没有报告错误的原因。

要检查使用代码读取数据(例如使用普通的ADO.Net),您将看到存储了所有数据。

查看一些解决方案: http://blog.extreme-advice.com/2013/03/05/error-fix-unable-to-show-xml-the-following-error-happened-there-is-an-unclosed-literal-string/

https://www.mssqltips.com/sqlservertip/2795/prevent-truncation-of-dynamically-generated-results-in-sql-server-management-studio/

SQL Server truncation and 8192 limitation