Самый эффективный способ получить строку данных из DB в ASP.NET

К сожалению, нет общедоступного API для перемещения стрелки вправо от TitledPane. Это не означает, что это невозможно, однако нам просто нужно динамически перевести стрелку, используя привязки. Чтобы остальная часть заголовка выглядела правильно, нам также нужно будет перевести текст и графику, если они есть, влево. Самый простой способ сделать все это - создать подкласс TitledPaneSkin и получить доступ к внутренним частям «области заголовка».

Вот пример реализации. Это позволяет вам позиционировать стрелку слева или справа с помощью CSS. Он также реагирует на изменение размеров, а также выравнивание и графические изменения.

package com.example;

import static javafx.css.StyleConverter.getEnumConverter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.css.CssMetaData;
import javafx.css.SimpleStyleableObjectProperty;
import javafx.css.StyleableObjectProperty;
import javafx.css.StyleableProperty;
import javafx.scene.Node;
import javafx.scene.control.Skin;
import javafx.scene.control.TitledPane;
import javafx.scene.control.skin.TitledPaneSkin;
import javafx.scene.layout.Region;
import javafx.scene.text.Text;

public class CustomTitledPaneSkin extends TitledPaneSkin {

    public enum ArrowSide {
        LEFT, RIGHT
    }

    /* ********************************************************
     *                                                        *
     * Properties                                             *
     *                                                        *
     **********************************************************/

    private final StyleableObjectProperty<ArrowSide> arrowSide
            = new SimpleStyleableObjectProperty<>(StyleableProperties.ARROW_SIDE, this, "arrowSide", ArrowSide.LEFT) {
        @Override protected void invalidated() {
            adjustTitleLayout();
        }
    };
    public final void setArrowSide(ArrowSide arrowSide) { this.arrowSide.set(arrowSide); }
    public final ArrowSide getArrowSide() { return arrowSide.get(); }
    public final ObjectProperty<ArrowSide> arrowSideProperty() { return arrowSide; }

    /* ********************************************************
     *                                                        *
     * Instance Fields                                        *
     *                                                        *
     **********************************************************/

    private final Region title;
    private final Region arrow;
    private final Text text;

    private DoubleBinding arrowTranslateBinding;
    private DoubleBinding textGraphicTranslateBinding;
    private Node graphic;

    /* ********************************************************
     *                                                        *
     * Constructors                                           *
     *                                                        *
     **********************************************************/

    public CustomTitledPaneSkin(TitledPane control) {
        super(control);
        title = (Region) Objects.requireNonNull(control.lookup(".title"));
        arrow = (Region) Objects.requireNonNull(title.lookup(".arrow-button"));
        text = (Text) Objects.requireNonNull(title.lookup(".text"));

        registerChangeListener(control.graphicProperty(), ov -> adjustTitleLayout());
    }

    /* ********************************************************
     *                                                        *
     * Skin Stuff                                             *
     *                                                        *
     **********************************************************/

    private void adjustTitleLayout() {
        clearBindings();
        if (getArrowSide() != ArrowSide.RIGHT) {
            // if arrow is on the left we don't need to translate anything
            return;
        }

        arrowTranslateBinding = Bindings.createDoubleBinding(() -> {
            double rightInset = title.getPadding().getRight();
            return title.getWidth() - arrow.getLayoutX() - arrow.getWidth() - rightInset;
        }, title.paddingProperty(), title.widthProperty(), arrow.widthProperty(), arrow.layoutXProperty());
        arrow.translateXProperty().bind(arrowTranslateBinding);

        textGraphicTranslateBinding = Bindings.createDoubleBinding(() -> {
            switch (getSkinnable().getAlignment()) {
                case TOP_CENTER:
                case CENTER:
                case BOTTOM_CENTER:
                case BASELINE_CENTER:
                    return 0.0;
                default:
                    return -(arrow.getWidth());
            }
        }, getSkinnable().alignmentProperty(), arrow.widthProperty());
        text.translateXProperty().bind(textGraphicTranslateBinding);

        graphic = getSkinnable().getGraphic();
        if (graphic != null) {
            graphic.translateXProperty().bind(textGraphicTranslateBinding);
        }
    }

    private void clearBindings() {
        if (arrowTranslateBinding != null) {
            arrow.translateXProperty().unbind();
            arrow.setTranslateX(0);
            arrowTranslateBinding.dispose();
            arrowTranslateBinding = null;
        }
        if (textGraphicTranslateBinding != null) {
            text.translateXProperty().unbind();
            text.setTranslateX(0);
            if (graphic != null) {
                graphic.translateXProperty().unbind();
                graphic.setTranslateX(0);
                graphic = null;
            }
            textGraphicTranslateBinding.dispose();
            textGraphicTranslateBinding = null;
        }
    }

    @Override
    public void dispose() {
        clearBindings();
        unregisterChangeListeners(getSkinnable().graphicProperty());
        super.dispose();
    }

    /* ********************************************************
     *                                                        *
     * Stylesheet Handling                                    *
     *                                                        *
     **********************************************************/

    public static List<CssMetaData<?, ?>> getClassCssMetaData() {
        return StyleableProperties.CSS_META_DATA;
    }

    @Override
    public List<CssMetaData<?, ?>> getCssMetaData() {
        return getClassCssMetaData();
    }

    private static class StyleableProperties {

        private static final CssMetaData<TitledPane, ArrowSide> ARROW_SIDE
                = new CssMetaData<>("-fx-arrow-side", getEnumConverter(ArrowSide.class), ArrowSide.LEFT) {

            @Override
            public boolean isSettable(TitledPane styleable) {
                Property<?> prop = (Property<?>) getStyleableProperty(styleable);
                return prop != null && !prop.isBound();
            }

            @Override
            public StyleableProperty<ArrowSide> getStyleableProperty(TitledPane styleable) {
                Skin<?> skin = styleable.getSkin();
                if (skin instanceof CustomTitledPaneSkin) {
                    return ((CustomTitledPaneSkin) skin).arrowSide;
                }
                return null;
            }

        };

        private static final List<CssMetaData<?, ?>> CSS_META_DATA;

        static {
            List<CssMetaData<?,?>> list = new ArrayList<>(TitledPane.getClassCssMetaData().size() + 1);
            list.addAll(TitledPaneSkin.getClassCssMetaData());
            list.add(ARROW_SIDE);
            CSS_META_DATA = Collections.unmodifiableList(list);
        }

    }

}

Затем вы можете применить этот скин ко всем TitledPane в вашем приложении из CSS, например, так:

.titled-pane {
    -fx-skin: "com.example.CustomTitledPaneSkin";
    -fx-arrow-side: right;
}

/*
 * The arrow button has some right padding that's added
 * by "modena.css". This simply puts the padding on the
 * left since the arrow is positioned on the right.
 */
.titled-pane > .title > .arrow-button {
    -fx-padding: 0.0em 0.0em 0.0em 0.583em;
}

Или вы можете настроить таргетинг только на определенные TitledPane с помощью добавления стиля. класс и использование указанного класса вместо .titled-pane.

Вышесказанное работает с JavaFX 11 и, вероятно, с JavaFX 10 и 9. Чтобы заставить его скомпилироваться на JavaFX 8, вам нужно изменить некоторые вещи:

  • Вместо этого импортировать com.sun.javafx.scene.control.skin.TitledPaneSkin.

    • Классы скинов были опубликованы в JavaFX 9.
  • Удалить вызовы на registerChangeListener(...) и unregisterChangeListeners(...). Я считаю правильным заменить их следующим:

    @Override
    protected void handleControlPropertyChange(String p) {
        super.handleControlPropertyChange(p);
        if ("GRAPHIC".equals(p)) {
            adjustTitleLayout();
        }
    }
    
  • Используйте new SimpleStyleableObjectProperty<ArrowSide>(...) {...} и new CssMetaData<TitledPane, ArrowSide>(...) {...}.

    • Вывод типа был улучшен в более поздних версиях Java.
  • Использовать (StyleConverter<?, ArrowSide>) getEnumConverter(ArrowSide.class).

    • В общей подписи getEnumConverter была ошибка, исправленная в более поздней версии. Использование броска работает вокруг проблемы. Вы можете @SuppressWarnings("unchecked") сыграть.

Проблема: Даже с учетом вышеуказанных изменений в JavaFX 8 возникает проблема - стрелка переводится только после фокусировки TitledPane. Похоже, что это не проблема с приведенным выше кодом, поскольку даже изменение свойства alignment не приводит к обновлению TitledPane до тех пор, пока оно не будет сфокусировано (даже если не используется вышеуказанный скин, а скорее только скин по умолчанию) , Мне не удалось найти решение этой проблемы (при использовании пользовательского скина), но, возможно, вы или кто-то еще можете. Я использовал Java 1.8.0_202 при тестировании на JavaFX 8.


Если вы не хотите использовать пользовательский скин или используете JavaFX 8 (это приведет к переводу стрелки без необходимости сначала фокусировать TitledPane), вы можете извлечь необходимые код, с некоторыми изменениями, в служебный метод:

public static void putArrowOnRight(TitledPane pane) {
    Region title = (Region) pane.lookup(".title");
    Region arrow = (Region) title.lookup(".arrow-button");
    Text text = (Text) title.lookup(".text");

    arrow.translateXProperty().bind(Bindings.createDoubleBinding(() -> {
        double rightInset = title.getPadding().getRight();
        return title.getWidth() - arrow.getLayoutX() - arrow.getWidth() - rightInset;
    }, title.paddingProperty(), title.widthProperty(), arrow.widthProperty(), arrow.layoutXProperty()));
    arrow.setStyle("-fx-padding: 0.0em 0.0em 0.0em 0.583em;");

    DoubleBinding textGraphicBinding = Bindings.createDoubleBinding(() -> {
        switch (pane.getAlignment()) {
            case TOP_CENTER:
            case CENTER:
            case BOTTOM_CENTER:
            case BASELINE_CENTER:
                return 0.0;
            default:
                return -(arrow.getWidth());
        }
    }, arrow.widthProperty(), pane.alignmentProperty());
    text.translateXProperty().bind(textGraphicBinding);

    pane.graphicProperty().addListener((observable, oldGraphic, newGraphic) -> {
        if (oldGraphic != null) {
            oldGraphic.translateXProperty().unbind();
            oldGraphic.setTranslateX(0);
        }
        if (newGraphic != null) {
            newGraphic.translateXProperty().bind(textGraphicBinding);
        }
    });
    if (pane.getGraphic() != null) {
        pane.getGraphic().translateXProperty().bind(textGraphicBinding);
    }
}

Примечание: хотя при этом стрелка справа помещается без необходимости сначала фокусировать TitledPane, TitledPane все еще страдает от проблема отмечена выше. Например, изменение свойства alignment не обновляет TitledPane, пока оно не будет сфокусировано. Я предполагаю, что это просто ошибка в JavaFX 8.

Этот способ работы не так «прост», как скин-подход, и требует двух вещей:

  1. TitledPane должен использовать значение по умолчанию TitledPaneSkin.
  2. TitledPane должно должно отображаться в Window (окно было , показывающее ) до вызова метода утилиты. [1176 ]

    • Из-за ленивой природы элементов управления JavaFX обложка и связанные с ней узлы не будут созданы, пока элемент управления не будет отображен в окне. Вызов вспомогательного метода до отображения элемента управления приведет к выбрасыванию NullPointerException, так как вызовы lookup вернут null.
    • При использовании FXML обратите внимание, что метод initialize вызывается во время вызова FXMLLoader.load (любой из перегрузок). Это означает, что при нормальных обстоятельствах созданные узлы еще не могут быть частью Scene, не говоря уже о показе Window. Вы должны подождать, пока сначала не отобразится TitledPane, затем , а затем вызвать служебный метод.

      Ожидание отображения TitledPane может быть достигнуто путем прослушивания свойства Node.scene, свойства Scene.window и свойства Window.showing (или вы можете прослушивать события WindowEvent.WINDOW_SHOWN). Однако, , если вы немедленно поместите загруженные узлы в показ Window , то вы можете отказаться от наблюдения за свойствами; вызовите служебный метод внутри вызова Platform.runLater изнутри initialize.

При использовании подхода с использованием скинов вся проблема ожидания окна показа исключается.


Обычное предупреждение. Этот ответ опирается на внутреннюю структуру TitledPane, которая может измениться в будущем выпуске. Будьте осторожны при изменении версий JavaFX. Я только (несколько) протестировал это на JavaFX 8u202 и JavaFX 11.0.2.

5
задан CountZero 16 April 2009 в 17:40
поделиться

5 ответов

как дела?

Есть ли причина, по которой у вас нет контейнеров объектов, представляющих строку в вашем база данных? С созданием пользовательского объекта гораздо проще работать на других уровнях вашего решения. Таким образом, благодаря этому подходу есть два очень жизнеспособных решения ваших проблем.

Скажем, у вас есть пользовательский объект, который представляет Продукт в вашей базе данных. Вы бы определили объект следующим образом:

public class Product {
    public int ProductID { get; set; }
    public string Name { get; set; }
    public byte[] Image { get; set; }
}

И заполнили бы коллекцию Продуктов (Коллекция) следующим образом:

var collection = new Collection<Product>();

using (var reader = command.ExecuteReader()) {
    while (reader.Read()) {
        var product = new Product();

        int ordinal = reader.GetOrdinal("ProductID");
        if (!reader.IsDBNull(ordinal) {
            product.ProductID = reader.GetInt32(ordinal);
        }

        ordinal = reader.GetOrdinal("Name");
        if (!reader.IsDBNull(ordinal)) {
            product.Name = reader.GetString(ordinal);
        }

        ordinal = reader.GetOrdinal("Image");
        if (!reader.IsDBNull(ordinal)) {
            var sqlBytes = reader.GetSqlBytes(ordinal);
            product.Image = sqlBytes.Value;
        }

        collection.Add(product);
    }
}

Обратите внимание, что я получаю значение через читатель Get x где x - это тип, который я хочу получить из столбца. Это рекомендуемый Microsoft способ получения данных для столбца в соответствии с http://msdn.microsoft.com/en-us/library/haa3afyz. aspx (второй абзац), поскольку извлеченное значение не нужно помещать в System.Object и распаковывать в примитивный тип.

Поскольку вы упомянули, что этот метод будет вызываться много, много раз в ASP Приложение .NET, вы можете пересмотреть такой общий подход, как этот. Метод, который вы используете для возврата NameValueCollection , очень неэффективен в этом сценарии (и, возможно, во многих других сценариях). Не говоря уже о том, что вы преобразуете каждый столбец базы данных в строку без учета культуры текущего пользователя, и культура является важным фактором в приложении ASP.NET. Я бы сказал, что эта NameValueCollection не должна использоваться и в других ваших усилиях по разработке. Я мог бы продолжать и продолжать об этом, но я спасу вас от моего беспорядка.

Конечно, если вы собираетесь создавать объекты, которые непосредственно отображаются в ваши таблицы, вы можете также обратиться к LINQ to SQL или ADO.NET Entity Framework . Вы будете счастливы, что сделали.

7
ответ дан 13 December 2019 в 05:42
поделиться

Вы получите гораздо больше пользы от кэширования данных, чем попытка оптимизировать возвращение одной строки. Если вы выбираете по первичному ключу, маловероятно, что вы увидите разницу между возвратом DataTable, DataRow или пользовательского объекта. Это кажется мне преждевременной оптимизацией. Я был бы более определенным, но я не уверен, что изменение байтового массива в миксе меняет дело.

0
ответ дан 13 December 2019 в 05:42
поделиться

То, что вы демонстрируете, - это запах кода, называемый Примитивная одержимость . Создайте пользовательский тип и верните его из метода репозитория. Не пытайтесь быть чрезмерно универсальными ... вы просто столкнетесь с этой сложностью в своем бизнес-коде, потому что вы будете взаимодействовать со своими сущностями, используя чисто процедурный код. Лучше создавать объекты, которые моделируют ваш бизнес.

Если вас беспокоит слишком большое количество кода доступа к данным, обратите внимание на использование инфраструктуры ORM, чтобы позаботиться о том, чтобы сгенерировать ее для вас. Вы не должны позволять этой проблеме диктовать плохой дизайн на уровне приложений.

2
ответ дан 13 December 2019 в 05:42
поделиться

С точки зрения эффективности кода, вы, вероятно, сделали это за наименьшее количество нажатий клавиш, и хотя это кажется расточительным, вероятно, самый простой в обслуживании. Тем не менее, если вы заинтересованы только в эффективности выполнения только того, что строго необходимо, вы можете создать облегченную структуру / класс для заполнения данными и использовать нечто похожее на:

public class MyAsset
{
    public int ID;
    public string Name;
    public string Description;
}

public MyAsset GetAsset(IDBConnection con, Int AssetId)
{
    using (var cmd = con.CreateCommand("sp_GetAsset"))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add(cmd.CreateParameter("AssetID"));
        using(IDataReader dr = cmd.ExecuteReader())
        {
            if (!dr.Read()) return null;

            return new MyAsset() { 
                ID = dr.GetInt32(0), 
                Name = dr.GetString(1), 
                Description = dr.GetString(2)
            };
        }
    }
}

Аналогично, вы можете выгружать данные аналогичным образом прямо в ваша коллекция KVP ...

Она выглядит не так чисто, как ваш оригинальный код, но она не создает всю таблицу только для того, чтобы получить единственную строку ...

Как уже упоминалось в другом посте относительно хотя запах кода, вероятно, я бы не стал передавать команду в качестве параметра, я думаю, что я бы с большей вероятностью инкапсулировал команду внутри этого метода, передать только соединение с базой данных и идентификатор актива, который я хотел - при условии, что я, конечно, не использовал кэширование, и передать обратно экземпляр MyAsset. Это делает метод достаточно универсальным, чтобы его можно было использовать с любым типом базы данных - конечно, при условии, что сохраненный процесс существует. Таким образом, остальная часть моего кода защищена от необходимости знать что-либо о базе данных, кроме того, какой это тип базы данных ... и в остальной части моего приложения я могу ссылаться на информацию об активах, используя MyAssetInstance.ID, MyAssetInstance.Name, MyAssetInstance. Описание и т. Д.

3
ответ дан 13 December 2019 в 05:42
поделиться

Спасибо за все отзывы, ребята. Я знаю, что ORM - это, пожалуй, верный путь, и этот и MVC фреймворк находятся в моем списке.

Чтобы дать немного больше подробностей, код, который я показываю, взят из раздела помощников в моем слое доступа к данным, который затем передает коллекцию значений строк или имен в бизнес-уровень для превращения в объекты.

Я думаю, что примеры кода mnero0429 и balabaster дают мне правильное направление. Используйте datareader и вручную выводите данные таким образом, не возиться с промежуточными объектами. Спасибо за подробную ссылку на MS mnero0429. Справедливо в отношении основной одержимости - хотя я действительно делаю из нее правильный класс активов на бизнес-уровне;)

Я также буду изучать структуру сущностей ADO.

Еще раз,

0
ответ дан 13 December 2019 в 05:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: