【问题标题】:Why can I only read properties and not set properties from ASP.NET web app when using .NET remoting?为什么在使用 .NET 远程处理时只能从 ASP.NET Web 应用程序读取属性而不能设置属性?
【发布时间】:2009-11-02 18:12:18
【问题描述】:

我在尝试在 Windows 服务中托管的远程对象上设置属性时遇到问题。我正在尝试更改对象的属性,但由于某种原因它没有保存。

这里是服务的相关代码:

    private static List<Alert> _alerts = new List<Alert>(); // List of the Alerts
    private TcpChannel _tcpChannel;

    protected override void OnStart(string[] args)
    {
        loadAlerts(); // This sets up the List (code not req'd)
        // Set up the remotelister so that other processes can access _alerts
        // Create the TcpChannel
        _tcpChannel = new TcpChannel(65000);
        ChannelServices.RegisterChannel(_tcpChannel, false);

        // Register the Proxy class for remoting.
        RemotingConfiguration.RegisterWellKnownServiceType(
          typeof(RemoteLister),
          "AlertList.soap",
          WellKnownObjectMode.Singleton);
    }

    [Serializable]
    public class RemoteLister : MarshalByRefObject
    {
        public List<Alert> TheList
        {
            get { return _alerts; }
            set { _alerts = value; }
        }

        public bool save()
        {
            EventLog.WriteEntry("AlertService", "Calling saveAlerts...");
            return saveAlerts();
        }
    }

这是 Alert 类的代码(还有很多其他内容):

    private string _alertName; // Name of alert

    public string AlertName
    {
        get { return _alertName; }
        set { _alertName = value; }
    }

现在在我的 ASP.NET Web 应用程序中,我是这样初始化所有内容的:

AlertService.RemoteLister remoteAlertList;

    protected void Page_Load(object sender, EventArgs e)
    {
        // This is where we create a local object that accesses the remote object in the service
        Type requiredType = typeof(AlertService.RemoteLister);
        // remoteAlertList is our reference to the List<Alert> in the always running service
        remoteAlertList = (AlertService.RemoteLister)Activator.GetObject(requiredType,
                "tcp://localhost:65000/AlertList.soap");
    }

所以现在下面的代码可以工作了:

private void fillFields()
    {
        AlertNameTextBox.Text = remoteAlertList.TheList[AlertDropDownList.SelectedIndex].AlertName;
    }

但是当我如下更改该属性时,它不起作用。

 protected void AlertSaveButton_Click(object sender, EventArgs e)
    {
        remoteAlertList.TheList[AlertDropDownList.SelectedIndex].AlertName = AlertNameTextBox.Text;
    }

有人知道它为什么不保存该属性吗?

提前致谢!

【问题讨论】:

  • 我对你的 cmets 添加了一些进一步的解释

标签: c# asp.net remoting .net-remoting


【解决方案1】:

我会因为List&lt;T&gt; 不继承自MarshalByRefObject,所以当您调用属性remoteAlertList.TheList 时,您会得到一个断开连接的对象。或许可以向对象添加一个索引器:

public class RemoteLister : MarshalByRefObject
{
    public Alert this[int index] {get {...} set {...}}
}

实际上,我会主要说放弃远程处理并使用 WCF/SOA。 RemoteLister 成为 [Serializable] 并没有什么意义。您可能还想考虑线程安全。


澄清; Alert 实例是独立的,因此本地更新不会影响服务器;基本上,您有两种情况:

  • 如果类型是MarshalByRefObject,那么它只存在于服务器;所有客户端操作都远程到实际对象 - 但适用于MarshalByRefObjecf 类型
  • 否则,对象将被序列化并重建一个实际对象。

如果你是一个索引器(例如)

obj[index].Name = "abc";

那么这是:

var tmp = obj[index]; // tmp points to the deserialized disconnected Alert
tmp.Name = "abc"; // we update the local disconnected Alert

(如果 AlertMarshalByRefObject,这更新服务器,但不要这样做)。但是如果我们将值推回

obj[index] = tmp;

然后我们更新了服务器。

正如您所发现的,以操作为中心的设计可能要简单得多(即setAlertName)。但我真的认为远程处理在这里是个坏主意。

【讨论】:

  • 尝试将其作为索引器...没有运气。关于可序列化的建议,Marc - 你是对的,它没有任何用途。我对 C# 很陌生,起初访问它时遇到错误,说它需要可序列化,所以我只是在此处添加 [Serializable] 并添加到实际的 Alert 类中,并且它起作用了。我想我只需要在警报课上使用它。至于线程安全,好主意。虽然它可能一次不会被多个线程访问,但有可能,我会调查一下。
  • 为什么您认为远程处理是个坏主意?这似乎是实现这一点的最简单方法......我简要地研究了 WCF,它看起来像是很多额外的工作和开销。如果这有所不同,Windows 服务和 Web 应用程序位于同一台服务器上。我正在学习,我想以“最好”的方式做到这一点,所以我很想听听你关于如何做到这一点的建议。
  • WCF 的设置并不比远程处理更难(也没有更多的开销)。在许多方面更简单。如果它适用于远程处理,那么很好。事实上,不必浏览防火墙让事情变得更简单。 general 我相信 WCF 具有更大的灵活性,但如果您不需要它...(毕竟我没有拒绝回答远程问题)。也许只是把它当作“也考虑 WCF”,听起来你已经完成了......
【解决方案2】:

尝试了索引器,但没有运气。我尝试了下面显示的另一种方法并且它有效,但这似乎是一种解决方法,而不是正确的方法。我所做的只是将它添加到 RemoteLister 类中:

public void setAlertName(int index, string name)
            {
                _alerts[index].AlertName = name;
            }

然后将保存按钮改成如下:

protected void AlertSaveButton_Click(object sender, EventArgs e)     
{     
    remoteAlertList.setAlertName(AlertDropDownList.SelectedIndex, AlertNameTextBox.Text);    
} 

我想这会让我暂时通过,但如果有人知道为什么它在我第一次这样做时不起作用,我宁愿以“正确”的方式来做。

【讨论】:

  • 仅供参考...一个可以帮助您以“正确”方式做事的免费工具是 FxCop。它会分析您的代码,并会推荐 Visual Studio 不会推荐的内容。例如,它会告诉您不要通过属性公开您的通用列表。相反,使用索引器来设置项目,并为获取公开一个只读列表。这使您可以控制将什么以及何时添加到列表中。如果你公开它,任何消耗你的类的东西都可以随时改变它的状态。只是一些想法。
  • 谢谢亚伦,我去看看!我非常感谢这个董事会的帮助!
【解决方案3】:

我查看了您的代码,它似乎是正确的。 但是可能导致错误的代码行在remoteAlertList初始化中:

    remoteAlertList = (AlertService.RemoteLister)Activator.GetObject(requiredType, "tcp://localhost:65000/AlertList.soap");

您已经使用初始 .net 转换方法“(newType)obj”和 Activator.GetObject(..) 初始化了一个 remoteAlertList 类,其中一个不返回原始 obj 的引用 [我认为它是 Activator。获取对象(..)]。它返回“AlertService.RemoteLister”类的精确副本,因此当您调用“TheList”时,您调用了它的精确副本,当您调用 Save() 时,它保存在复制的对象中而不是真实对象中。

在这里我看不到您的完整代码,但据我所知,我可以告诉您解决方案是找到一种方法来从主机访问 AlertService.RemoteLister 对象的引用。

【讨论】:

    猜你喜欢
    • 2013-06-08
    • 2011-07-21
    • 2020-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-01
    • 1970-01-01
    相关资源
    最近更新 更多