什么可能导致WCF中的"无法访问已处置的对象"错误?

Ari*_*ian 15 c# wcf objectdisposedexception

我使用以下代码:

private WSHttpBinding ws;
private EndpointAddress Srv_Login_EndPoint;
private ChannelFactory<Srv_Login.Srv_ILogin> Srv_LoginChannelFactory;
private Srv_Login.Srv_ILogin LoginService;
Run Code Online (Sandbox Code Playgroud)

Login是我的构造函数:

public Login()
        {
            InitializeComponent(); 
            ws = new WSHttpBinding();
            Srv_Login_EndPoint = new EndpointAddress("http://localhost:2687/Srv_Login.svc");
            Srv_LoginChannelFactory = new ChannelFactory<Srv_Login.Srv_ILogin>(ws, Srv_Login_EndPoint);
        }
Run Code Online (Sandbox Code Playgroud)

我正在以这种方式使用服务:

private void btnEnter_Click(object sender, EventArgs e)
{
    try
    {

        LoginService = Srv_LoginChannelFactory.CreateChannel();
        Srv_Login.LoginResult res = new Srv_Login.LoginResult();
        res = LoginService.IsAuthenticated(txtUserName.Text.Trim(), txtPassword.Text.Trim());
        if (res.Status == true)
        {
            int Id = int.Parse(res.Result.ToString());
        }
        else
        {
            lblMessage.Text = "Not Enter";
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    finally
    {
        Srv_LoginChannelFactory.Close();
    }
}
Run Code Online (Sandbox Code Playgroud)

当用户输入有效的用户名和密码时,一切都很好.当用户输入错误的用户名和密码时,第一次尝试正确显示"未输入"消息,但在第二次尝试时,用户会看到以下消息:

{System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.ServiceModel.ChannelFactory`1[Test_Poosesh.Srv_Login.Srv_ILogin]'.
   at System.ServiceModel.Channels.CommunicationObject.ThrowIfDisposed()
   at System.ServiceModel.ChannelFactory.EnsureOpened()
   at System.ServiceModel.ChannelFactory`1.CreateChannel(EndpointAddress address, Uri via)
   at System.ServiceModel.ChannelFactory`1.CreateChannel()
Run Code Online (Sandbox Code Playgroud)

如何修复代码以防止发生此错误?

Con*_*rix 16

Srv_LoginChannelFactory.Close()是它被处置的地方.当你打电话给你时,你放弃了你拥有的任何非托管资源.尝试执行其他操作然后检查其状态或重新打开它会导致"无法访问已处置对象"异常.

每当你关闭一次性物体并尝试用它做一些事情时,这都是正确的.例如,写入已关闭的文件,或在已关闭的数据库连接上执行sql语句.

要解决这个问题,您有三种选择.

  1. 不要将Srv_LoginChannelFactory设为字段.而是将其设置为按钮单击的本地.如果这是您使用它的唯一地方,这可能是有意义的,因为它缩短了您使用非托管资源的时间.

  2. 实现IDisposable(只要你有一个Disposable字段就可以这样做)除了Login.Dispose之外,不要关闭Srv_LoginChannelFactory.

  3. 在尝试使用它创建通道之前,请更改按钮单击以检查Srv_LoginChannelFactory的状态.如果没有按钮单击,您仍然需要实现IDisposable.

注意: EnsureOpened看起来可以用于检查状态,但它仅在打开之前有效.一旦关闭它就会抛出.

关于Close()与Dispose相同.

从开发类库设计指南中的实现完成和处理以清理非托管资源的 "自定义处理方法名称"一节

有时,特定于域的名称比Dispose更合适.例如,文件封装可能希望使用方法名称Close.在这种情况下,请私下实现Dispose并创建一个调用Dispose的公共Close方法.以下代码示例说明了此模式.您可以使用适合您的域的方法名称替换Close.此示例需要System命名空间.

这里的想法是给Open方法赋予奇偶校验.我个人认为这会造成很多混乱,但我想不出更好的事情(CloseAndDispose?)