The proper place to disconnect the event would, as you suspect, be in the IDisposable.Dispose method of A. There isn't really any good alternative. It would be useless to try to disconnect the event within A's finalizer/destructor, since the only way A would ever be eligible for garbage collection prior to its disconnecting from B would be for B to become itself eligible for garbage collection; once that has happened, the subscription would become a moot point so it wouldn't matter whether it was cleaned up or not.
If it's necessary for events from abandoned short-lived subscribers to be cleaned up within the lifetime of a long-lived publisher, there are ways of doing that if the publishing and subscribing classes cooperate. For example, the event subscriber's public face can be a wrapper object which holds a reference to the actual event subscriber, with the subscriber itself not holding a strong reference to that public object. Under that scenario, the event subscription would not prevent the public object from falling out of scope and becoming eligible for finalization. A finalizer in the public object could notify the subscribing object that the subscription would no longer be needed and it should unsubscribe. Since such notification would arrive asynchronously, the object publishing the subscription would have to offer a means by which subscribers could be asynchronously unsubscribed. This could be done by a normal "unsubscribe" request, provided that the publishing event guaranteed it to be non-blocking and thread-safe; unfortunately, most events offer no such guarantee.