kzen.dev
  • Вопросы
  • Метки
  • Пользователи
Оповещения
Вознаграждения
Регистрация
После регистрации, сможете получать уведомления об ответах и комментариях на Ваши вопросы.
Вход
Если у Вас уже есть аккаунт, войдите чтобы проверить новые уведомления.
Тут будут вознаграждения за добавленные вопросы, ответы и комментарий.
Дополнительно
Источник
Редактировать
 michael
michael
Вопрос

Динамическое создание GridView

Я хочу создать GridView, который отображает записи для PDF файлов. К этим записям могут быть прикреплены метаданные, настраиваемые пользователем, так что он может создавать свои собственные столбцы и вводить туда свою информацию. Затем я хочу, чтобы они отображались в GridView, чтобы они могли упорядочить каждый столбец и порядок столбцов, если порядок столбцов равен -1, он не будет отображаться в GridView.

Например, есть статическая таблица

DocumentsTable:
ID int
PDF_Folder varchar
UserID int

Затем есть другая таблица, для которой пользователи могут создавать свои собственные столбцы

MetaDataColumns:
ID int
userid int foreign key
KeyName varchar
Order int

и таблицу для хранения значений

MetaDataValues:
ID int
UserID int foreign key
DocumentID int foreign key
MetaDataID int foreign key
value varchar(100)

Теперь проблема в том, что мне нужно получить столбцы из MetaDataColumn для создания GridView, а затем заполнить его значениями из таблицы MetaDataValue. Мой первоначальный план состоит в том, чтобы иметь функцию, которая динамически создает GridView и добавляет в него столбцы, однако я застрял на том, как использовать значения в MetaDataValue в качестве столбцов. В качестве альтернативы я мог бы просто заставить GridView автоматически генерировать столбцы, но мне нужно настроить SQL для отображения пользовательских данных. Я немного запутался, как вообще к этому подступиться.

Один из подходов, который я придумал, заключается в следующем псевдокоде:

private DataTable CreateColumns()
{
   var columns =  select * from MetaDataColumns 
                  where userid = UserId;

   DataTable dt = new DataTable();

   foreach (column in columns)
   {
       dt.Columns.Add(new DataColumn(column[keyName], typeof(string));  //assumes all string
   }

 return dt
}

private void PopulateDG(DataGrid dg)
{
    var documents = select * from DocumentsTable
                     where userid=UserId;

    foreach (document in documents)
    {            
        var columnValues = select * from MetaDatavalues 
                           documentID == document.id;

        DataRow dr = dg.NewRow();
        dr[columnValues.KeyName] = columnValues.value;

    }

 }

 private void LoadGV()
 {  
   DataGrid dg = CreateColumns();
   PopulateDG(dg);
   GridView.datasource = dg;
   GridView.DataBind();
  }

Одна из вещей, которая мне не нравится в этой схеме, заключается в том, что для каждой строки в таблице документов создается еще один запрос. Я не уверен, является ли это проблемой SQL?

8 2012-07-11T05:22:54+00:00 3
 victor
victor
Редактировал вопрос 13-го июля 2012 в 11:26
Программирование
linq
asp.net
c#
database-design
Решение / Ответ
 Ben
Ben
14-го июля 2012 в 11:09
2012-07-14T23:09:46+00:00
Дополнительно
Источник
Редактировать
#16656428

Ваша проблема в основном связана с дизайном базы данных. Вам приходится динамически добавлять столбцы, потому что вы перевели то, что было бы столбцом (в 3НФ) в строку в ваших таблицах. Очевидно, это происходит потому, что вы позволяете пользователям добавлять свои собственные столбцы - мой разум содрогается, но так работает приложение :-).

Из-за структуры MetaDataColumns я собираюсь предположить, что у пользователя есть возможность определить набор имен колонок, которые он может затем применить к отдельному документу по своему усмотрению.

Я думаю, проблема в том, что, пытаясь нормализовать все должным образом в полностью де-нормализованной базе данных, вы создаете себе много хлопот. Моим решением было бы денормализовать вашу таблицу MetaDataValues. Вы не упомянули, какую РСУБД вы используете, но MySQL имеет жесткое ограничение 4096 столбцов или 65k байт. Ограничение в Oracle составляет 1000 и 1024 в SQL Server.

Если вы измените структуру MetaDataValues на следующую, вы сможете вместить туда как минимум 332 набора информации. Это будет отдельно уникальным по UserID, DocumentID, так что теоретически можно убрать суррогатный ключ ID.

MetaDataValues:
ID int
UserID int foreign key
DocumentID int foreign key
KeyName1 varchar
Order1 int
Value1 varchar(100) 
...
KeyNameN varchar
OrderN int
ValueN varchar(100)

Конечно, это устанавливает верхний предел на количество столбцов, которые вы можете позволить создавать отдельному пользователю, до 332; но это нормально - ограничивать возможности пользователей, которые могут сойти с ума, и любой, кто может придумать 332 отдельных бита метаданных для хранения в одном PDF, заслуживает того, чтобы быть как-то ограниченным.

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

Это означает, что MetaDataColumns не будет использоваться ни для чего, кроме отображения опций пользователей. Вам придется обновлять MetaDataValues при каждом изменении, а обеспечение того, что вы не перезаписываете уже существующую информацию, может быть немного болезненным. Я подозреваю, что вам придется делать что-то вроде выбора записи перед ее обновлением, итерации по KeyName1 ... KeyNameN и заполнения первой записи, в которой нет данных. Или же вы можете просто написать совершенно ужасный SQL-запрос. В любом случае, это станет "точкой пресечения".

Другим вариантом было бы добавить дополнительный столбец к MetaDataColumns, который указывал бы, к какому N относится столбец, но это ограничивает пользователя 332 столбцами в целом, а не 332 на документ.

Однако теперь выборка из базы данных стала безумно простой:

select d.*, m.*
  from DocumentsTable d
  join MetaDataValues m
    on d.ID = m.DocumentID
   and d.UserID = m.UserID
 where d.UserId = ?

Нет необходимости пытаться итерировать таблицы, динамически генерируя 1000 операторов выбора столбцов. Вся информация находится прямо здесь и легко доступна для вас.

В конце концов, "правильный" ответ на ваш вопрос зависит от того, на что вы хотите потратить время. Хотите ли вы, чтобы создание или обновление документа занимало на полсекунды больше времени, или на полсекунды (возможно, больше), чтобы выбрать информацию в этом документе.

Лично я думаю, что пользователи понимают, что создание чего-либо требует времени, но нет ничего более раздражающего, чем ждать целую вечность, пока что-то появится.

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

 Community
Community
Редактировал ответ 23-го мая 2017 в 11:56
2
0
 Nathan
Nathan
13-го июля 2012 в 7:49
2012-07-13T07:49:32+00:00
Дополнительно
Источник
Редактировать
#16656426

Вы имеете в виду

<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="false">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Path=Id}" Header="ID"/>
        <DataGridTextColumn Binding="{Binding Path=Name}" Header="Name"/>
    </DataGrid.Columns>
</DataGrid>

и создать класс в коде, например

public class MetadataSource
{
    public MetadataSource()
    { // use reflection to create properties/values }
}
1
0
Tianzhen Lin
Tianzhen Lin
14-го июля 2012 в 4:08
2012-07-14T16:08:01+00:00
Дополнительно
Источник
Редактировать
#16656427

Я определенно вижу проблему в вашем псевдокоде, где каждая строка данных представляет собой отдельный запрос. Когда у вас есть 1 000 строк данных, в итоге у вас будет 1 000+ запросов к базе данных, и ваша страница будет работать очень медленно.

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

var values = SELECT * FROM MetaDataValues
WHERE documentid IN (SELECT id FROM DocumentsTable WHERE userid = UserId)

foreach (val in values)
{            
    DataRow dr = dg.NewRow();
    ...
}

Я обычно не предпочитаю подход "SELECT *", он заставляет базу данных делать дополнительный запрос, чтобы заполнить все столбцы. В вашем случае, учитывая, что пользователь может быть ограничен столбцами, которые он может видеть, SQL во многих отношениях принесет больше данных, чем нужно. Поэтому ваш код может быть дополнительно консолидирован и оптимизирован, например:

private void PopulateDG(DataGrid dg)
{
    var columns = SELECT columnKey FROM MetaDataColumns
            WHERE userid = UserId;

    // pseudo code, join column keys into a comma delimited string
    string columnFields = string.Join(",", columns);

    string getValueSql = string.Format("SELECT {0} FROM MetaDataValues
            WHERE documentid IN (SELECT id FROM DocumentsTable WHERE userid = UserId)", columnFields);

    var values = ExecuteSql(getValueSql);
1
0
Похожие сообщества 20
DotNetRuChat
DotNetRuChat
6 652 пользователей
Чат русскоязычного .NET сообщества http://dotnet.ru/ Правила: https://t.me/DotNetRuChat/704399 Вам могут быть интересны: @dotnetchat, @cilchat, @fsharp_chat, @pro_net, @AvaloniaRU, @xamarin_russia, @DotNetRuJobs, @uwp_ru Флуд в @dotnettalks
Открыть telegram
DBA - русскоговорящее сообщество
DBA - русскоговорящее сообщество
3 542 пользователей
Общаемся и обсуждаем темы, посвященные DBA, PostgreSQL, Redis, MongoDB, MySQL, neo4j, riak и т.д. См. также: @devops_ru, @kubernetes_ru, @docker_ru, @nodejs_ru Рекомендуем сразу отключить уведомления, чтобы пребывание здесь было полезным и комфортным.
Открыть telegram
Вакансии .NET
Вакансии .NET
3 530 пользователей
Правила: https://t.me/DotNetRuJobs/123 Канал с вакансиями - https://t.me/DotNetRuJobsFeed Вам могут быть интересны: @dotnetruchat, @cilchat, @fsharp_chat, @pro_net, @AvaloniaRU, @xamarin_russia Флуд в @dotnettalks
Открыть telegram
Microsoft Stack Jobs
Microsoft Stack Jobs
2 414 пользователей
Work & freelance only Microsoft Stack. Feed https://t.me/Microsoftstackjobsfeed Чат про F#: @Fsharp_chat Чат про C#: @CSharpChat Чат про Xamarin: @xamarin_russia Чат общения:@dotnettalks
Открыть telegram
С#
С#
2 330 пользователей
Стараемся не флудить. Пишем по делу. Правила: https://t.me/professorweb/430450 Для флуда @svoboda_obsh
Открыть telegram
pro.net
pro.net
1 820 пользователей
Обсуждение .NET и всего, что с ним связано. Правила: не флудить не по теме, уважать ваших коллег и никакой рекламы (объявления о вакансиях можно согласовать с @AlexFails). https://t.me/pro_net/34653 Флудилка: @dotnettalks
Открыть telegram
Добавить вопрос
Категории
Все
Технологий
Культура / Отдых
Жизнь / Искусство
Наука
Профессии
Бизнес
Пользователи
Все
Новые
Популярные
1
Ilya Smirnov
Зарегистрирован 6 дней назад
2
Денис Васьков
Зарегистрирован 1 неделю назад
3
Dima Patrushev
Зарегистрирован 1 неделю назад
4
sirojidddin otaboyev
Зарегистрирован 2 недели назад
5
Елена Гайдамамакинат
Зарегистрирован 2 недели назад
RU
© kzen.dev 2023
Источник
stackoverflow.com
под лицензией cc by-sa 3.0 с атрибуцией