FIX bagging with metadata routing and estimator implement __len__
diff --git a/sklearn/ensemble/_bagging.py b/sklearn/ensemble/_bagging.py
index e0ff0b9509c3b..7f278cb06f2ba 100644
--- a/sklearn/ensemble/_bagging.py
+++ b/sklearn/ensemble/_bagging.py
@@ -113,8 +113,6 @@ def _parallel_build_estimators(
     estimators = []
     estimators_features = []
 
-    request_or_router = get_routing_for_object(ensemble.estimator_)
-
     # TODO: (slep6) remove if condition for unrouted sample_weight when metadata
     # routing can't be disabled.
     support_sample_weight = has_fit_parameter(ensemble.estimator_, "sample_weight")
@@ -164,9 +162,14 @@ def _parallel_build_estimators(
         # Note: Row sampling can be achieved either through setting sample_weight or
         # by indexing. The former is more efficient. Therefore, use this method
         # if possible, otherwise use indexing.
-        if (
-            _routing_enabled() and request_or_router.consumes("fit", ("sample_weight",))
-        ) or (not _routing_enabled() and support_sample_weight):
+        if _routing_enabled():
+            request_or_router = get_routing_for_object(ensemble.estimator_)
+            consumes_sample_weight = request_or_router.consumes(
+                "fit", ("sample_weight",)
+            )
+        else:
+            consumes_sample_weight = support_sample_weight
+        if consumes_sample_weight:
             # Draw sub samples, using sample weights, and then fit
             curr_sample_weight = _check_sample_weight(
                 fit_params_.pop("sample_weight", None), X
@@ -635,6 +638,9 @@ def get_metadata_routing(self):
     def _get_estimator(self):
         """Resolve which estimator to return."""
 
+    def _more_tags(self):
+        return {"allow_nan": _safe_tags(self._get_estimator(), "allow_nan")}
+
 
 class BaggingClassifier(ClassifierMixin, BaseBagging):
     """A Bagging classifier.
@@ -835,7 +841,9 @@ def __init__(
 
     def _get_estimator(self):
         """Resolve which estimator to return (default is DecisionTreeClassifier)"""
-        return self.estimator or DecisionTreeClassifier()
+        if self.estimator is None:
+            return DecisionTreeClassifier()
+        return self.estimator
 
     def _set_oob_score(self, X, y):
         n_samples = y.shape[0]
@@ -1059,14 +1067,6 @@ def decision_function(self, X):
 
         return decisions
 
-    def _more_tags(self):
-        if self.estimator is None:
-            estimator = DecisionTreeClassifier()
-        else:
-            estimator = self.estimator
-
-        return {"allow_nan": _safe_tags(estimator, "allow_nan")}
-
 
 class BaggingRegressor(RegressorMixin, BaseBagging):
     """A Bagging regressor.
@@ -1328,13 +1328,8 @@ def _set_oob_score(self, X, y):
         self.oob_prediction_ = predictions
         self.oob_score_ = r2_score(y, predictions)
 
-    def _more_tags(self):
-        if self.estimator is None:
-            estimator = DecisionTreeRegressor()
-        else:
-            estimator = self.estimator
-        return {"allow_nan": _safe_tags(estimator, "allow_nan")}
-
     def _get_estimator(self):
         """Resolve which estimator to return (default is DecisionTreeClassifier)"""
-        return self.estimator or DecisionTreeRegressor()
+        if self.estimator is None:
+            return DecisionTreeRegressor()
+        return self.estimator
diff --git a/sklearn/ensemble/tests/test_bagging.py b/sklearn/ensemble/tests/test_bagging.py
index 2c1e308cee33b..da855a568b402 100644
--- a/sklearn/ensemble/tests/test_bagging.py
+++ b/sklearn/ensemble/tests/test_bagging.py
@@ -10,14 +10,19 @@
 import numpy as np
 import pytest
 
+import sklearn
 from sklearn.base import BaseEstimator
 from sklearn.datasets import load_diabetes, load_iris, make_hastie_10_2
 from sklearn.dummy import DummyClassifier, DummyRegressor
 from sklearn.ensemble import (
+    AdaBoostClassifier,
+    AdaBoostRegressor,
     BaggingClassifier,
     BaggingRegressor,
     HistGradientBoostingClassifier,
     HistGradientBoostingRegressor,
+    RandomForestClassifier,
+    RandomForestRegressor,
 )
 from sklearn.feature_selection import SelectKBest
 from sklearn.linear_model import LogisticRegression, Perceptron
@@ -936,3 +941,36 @@ def fit(self, X, y):
 def test_bagging_allow_nan_tag(bagging, expected_allow_nan):
     """Check that bagging inherits allow_nan tag."""
     assert bagging._get_tags()["allow_nan"] == expected_allow_nan
+
+
+@pytest.mark.parametrize(
+    "model",
+    [
+        BaggingClassifier(
+            estimator=RandomForestClassifier(n_estimators=1), n_estimators=1
+        ),
+        BaggingRegressor(
+            estimator=RandomForestRegressor(n_estimators=1), n_estimators=1
+        ),
+    ],
+)
+def test_bagging_with_metadata_routing(model):
+    """Make sure that metadata routing works with non-default estimator."""
+    with sklearn.config_context(enable_metadata_routing=True):
+        model.fit(iris.data, iris.target)
+
+
+@pytest.mark.parametrize(
+    "model",
+    [
+        BaggingClassifier(
+            estimator=AdaBoostClassifier(n_estimators=1, algorithm="SAMME"),
+            n_estimators=1,
+        ),
+        BaggingRegressor(estimator=AdaBoostRegressor(n_estimators=1), n_estimators=1),
+    ],
+)
+def test_bagging_without_support_metadata_routing(model):
+    """Make sure that we still can use an estimator that does not implement the
+    metadata routing."""
+    model.fit(iris.data, iris.target)

I could catch a regression in `imbalanced-learn` when `estimator` in `Bagging*` implement `__len__` (e.g. `RandomForest*`) where `_get_estimator` will trigger calling `__len__` with the current pattern.

The problem is that `__len__` relies on fitted attribute while `_get_estimator` is called before `fit`.

This fix is check for `None` instead to know when to create a default estimator.

ping @adrinjalali @OmarManzoor @adam2392 since it was introduced in https://github.com/scikit-learn/scikit-learn/pull/28432

No changelog needed since we did not yet release this bug ;)