Что может привести к ошибке "Не удается получить доступ к удаленному объекту" в WCF?

Я использую следующий код:

private WSHttpBinding ws;
private EndpointAddress Srv_Login_EndPoint;
private ChannelFactory<Srv_Login.Srv_ILogin> Srv_LoginChannelFactory;
private Srv_Login.Srv_ILogin LoginService;

Вход - это мой конструктор:

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);
        }

И я использую сервис таким образом:

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();
    }
}

Когда пользователь вводит правильное имя пользователя и пароль, все в порядке. Когда пользователь вводит неправильное имя пользователя и пароль, первая попытка правильно отображает сообщение "Не вводить", но во второй попытке пользователь видит это сообщение:

{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()

Как я могу исправить свой код, чтобы предотвратить эту ошибку?

Ответ 1

Srv_LoginChannelFactory.Close() находится там, где он находится. Когда вы звоните, вы отказываетесь от неуправляемого ресурса. Попытка сделать что-то другое, а затем проверить его состояние или повторное открытие, приводит к исключению "Невозможно получить доступ к удаленному объекту".

Это верно, когда вы закрываете одноразовый объект и затем пытаетесь что-то сделать с ним. Например, запись в закрытый файл или выполнение инструкции sql при закрытом соединении с базой данных.

Чтобы решить эту проблему, у вас есть три варианта.

  • Не создавайте поле Srv_LoginChannelFactory a. Вместо этого сделайте его локальным нажатием кнопки. Если это единственное место, которое вы используете, это, вероятно, имеет смысл сделать, потому что это сокращает время, в течение которого вы используете неуправляемый ресурс.

  • Внедрение IDisposable (вы должны делать это каждый раз, когда у вас есть однопозиционное поле), не закрывайте Srv_LoginChannelFactory, кроме Login.Dispose.

  • Измените кнопку, чтобы проверить состояние Srv_LoginChannelFactory, прежде чем пытаться создать канал с ним. Вам все равно нужно реализовать IDisposable в случае, если нажатие кнопки не произойдет.

Примечание: EnsureOpened похоже, что он может использоваться для проверки состояния, но он работает только до его открыл. Когда он будет закрыт, он бросит.

Относительно Close() совпадает с Dispose.

В разделе "Настройка имени метода удаления" в Внедрение Finalize и Dispose для очистки неуправляемых ресурсов в Руководстве по разработке для разработки классов библиотек

Время от времени доменное имя более подходящий, чем Dispose. Для Например, инкапсуляция файла может хотите использовать метод name Close. В этот случай, реализовать Dispose в частном порядке и создать общедоступный метод Close, который вызовы Dispose. Следующий код пример иллюстрирует этот шаблон. Вы может заменить Close именем метода соответствующий вашему домену. Эта Например, требуется пространство имен System.

Идея здесь - дать четность методу Open. Лично я думаю, что это вызывает много путаницы, но я не могу придумать ничего лучше (CloseAndDispose?)

Ответ 2

Проблема здесь (я думаю, что Конрад пропустил) заключается в том, что Kerezo закрывает ChannelFactory (Srv_LoginChannelFactory), который закрывает (распоряжается) всеми его каналами, когда он, вероятно, хочет закрыть только канал (LoginService).

Итак, измените:

    Srv_LoginChannelFactory.Close();

в

    try
    {
        LoginService.Close();
    }
    catch
    {
        LoginService.Abort();
    }

Ответ 3

используйте Сериализованный и десериализованный перед использованием, который делает файл оригинальным. Для Сериализующего объекта

var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var SerializedFile = JsonConvert.SerializeObject(file, settings); 

и для десериализации объекта

var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };        
var getUserObj = JsonConvert.DeserializeObject<OBJECT_TYPE>("SERIALIZED_OBJ", settings);