Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM Новый топик    Ответить
 Сортировка в Entity framework  [new]
GraDea
Member

Откуда: Москва
Сообщений: 52
Всем привет!

Есть две таблицы, связанные один ко многим. Я добавил их в модель средствами VS

В коде я получаю экземпляр записи родительской таблицы, заполняю свойство-список дочерней и все это привязываю (Binding) в датагрид и прочие контролы в TwoWay режиме (используется wpf ). Это позволяет мне с минимумом кода и телодвижений иметь приложение для показа и редактирования данных.

Но есть ложка дегтя. Сортировка.

Сортировка записей дочерней таблицы по умолчанию меня не устраивает и она мне непонятна.
Если я
в ItemsSource отдаю список с методом MyList.OrderBy(...)
то невозможно редактирование.

Пытаюсь в модели написать
<EntitySet 
  Name="StudDiscipline" 
  EntityType="diplomModel.Store.StudDiscipline" 
  store:Type="Tables" 
  store:Schema="dbo" 
  store:Name="StudDiscipline">
  <DefiningQuery>SELECT 
     [id_discipline]
    ,[id_student]
    ,[Name]
    ,[Mark]
    ,[Hours]
    ,[OrderNumber]
    FROM [dbo].[StudDiscipline]
    ORDER BY [OrderNumber]
  </DefiningQuery>
</EntitySet>

но программа ругается

{"The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified."}

Контрол, который я использую (DataGrid из WPFToolbox) не поддерживает сортировку при работе с Entity Model и Linq2Sql

Подскажите как я могу выдать данные пользователю в нужном мне порядке при сохранении возможности редактирования!

Заранее спасибо!

С уважением,
Пешков Евгений.
11 июн 09, 15:12    [7290862]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
buser
Member

Откуда: Санкт-Петербург
Сообщений: 4537
бекоз лик оборачивает всю вашу конструкцию в скобочки и заталкивает во фром...?
он не такой умный? Зачем вам сдался ордер бай? указывайте через код порядок сортировки...
11 июн 09, 15:16    [7290900]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
SeVa
Member [заблокирован]

Откуда: Москва
Сообщений: 4324
Контролы WPF сами обеспечивают сортировку ICollectionView
11 июн 09, 23:21    [7292725]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
GraDea
Member

Откуда: Москва
Сообщений: 52
SeVa,
Как я понял таким образом я получу view без обратной связи?

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

Как мне проще всего это сделать?


buser
указывайте через код порядок сортировки...


MyList.OrderBy(...) не работает так как мне надо.

Или имелось ввиду что-то другое?
22 июн 09, 09:41    [7326379]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
SeVa
Member [заблокирован]

Откуда: Москва
Сообщений: 4324
При редактировании сортировку должен делать только контрол,в grid'e эта возможность есть.
Почитай для начала документацию
22 июн 09, 13:04    [7327772]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
SeVa
Member [заблокирован]

Откуда: Москва
Сообщений: 4324
автор
Как я понял таким образом я получу view без обратной связи?

И еще про ObservableCollection
22 июн 09, 13:05    [7327778]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
GraDea
Member

Откуда: Москва
Сообщений: 52
SeVa,
У Вас есть рабочий пример кода работы с ДатаГрид из WPFToolbox с сортировкой по нужному столбцу? Если есть, не могли бы Вы поделиться?

С уважением,
Пешков Евгений
23 июн 09, 09:29    [7331399]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
GraDea
Member

Откуда: Москва
Сообщений: 52
Нашел нечто подобное тому, что мне надо

http://blog.nicktown.info/2008/12/10/using-a-collectionviewsource-to-display-a-sorted-entitycollection.aspx

Пока разбираюсь.
23 июн 09, 09:43    [7331447]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
GraDea
Member

Откуда: Москва
Сообщений: 52
Вроде работает! Только почему-то один раз отрабатывает.

Я из тривью выбираю элемент, на основе выбора получаю элемент из Entity Model

private void TextBlock_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
TextBlock tbStudinTV = (TextBlock)sender;
Guid g = new Guid(tbStudinTV.Tag.ToString());
Student stud = (from student in dip.Student where student.ID_Student == g select student).First();
stud.StudDiscipline.Load();
base.DataContext = stud;
}

Когда я первый раз выбираю студента все дисциплины загружаются с сортировкой как надо. Но второй и последующие выборы выводят не сортированный список.
23 июн 09, 11:59    [7332228]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
SeVa
Member [заблокирован]

Откуда: Москва
Сообщений: 4324
Посмотрел ссылку,на мой взгляд, подход через одно место.Есть бесплатные гриды сторонних фирм,которые поддерживают сортировку без этих танцев с бубнами и дополнительными тормозами.
Поскольку только начинаешь, перейди на Silverlight, судя по всему, он идет в МС с более высоким приоритетом, чем WPF(грид впервые появился в нем).В SL для EF есть RIA Service.
Код,который ты привел в конце, выбрось и забудь.Binding WPF/SL позволяет полностью обходится без обработчиков клацанья мышкой.Примерный вариант возможного решения - MVVM
23 июн 09, 17:42    [7334548]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
GraDea
Member

Откуда: Москва
Сообщений: 52
SeVa,
То что лишний код, это конечно плохо и очень жаль, что дефолтный грид не поддерживает сортировку при работе с entity model. Будем надеяться, что ситуация исправиться, об этом баге известно.

Насчет MVVM и клацанья мышкой.
Что-то сразу об этом не подумал %). Остальное то все сделано именно через байдинги, а то что можно и DataContext передать - не сразу понял.

Только теперь проблема с отложенной загрузкой - пока не заполняются гриды, остальные поля заполняются.

огромное спасибо за помощь и советы!
29 июн 09, 09:03    [7353400]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
SeVa
Member [заблокирован]

Откуда: Москва
Сообщений: 4324
[url=Только теперь проблема с отложенной загрузкой - пока не заполняются гриды, остальные поля заполняются.
][/url]
Обычно в этом случае:
- в ViewModel или Model создают свойство IsBusy.Перед загрузкой его устанавливают в true, после - false
- в View добавляют всплывающий/перекрывающий BusyControl(погугли готовые реализации),его свойство Collapsed связывают с IsBusy с помощью BooleanToVisibilityConverter
29 июн 09, 13:39    [7354486]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
GraDea
Member

Откуда: Москва
Сообщений: 52
Я явно загружаю связанные списки в коде, ставлю брейкпоинт и вижу, что у связанных списков IsLoaded == true, но в ДатаГриде не вижу данных.
29 июн 09, 15:27    [7355125]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
SeVa
Member [заблокирован]

Откуда: Москва
Сообщений: 4324
Так бы сразу и сказал


public class Student
{
   
   public ObservableCollection<Discipline> Disciplines
   { 
        get { return _desciplines ; } 
   }
   public LoadDisciplines()
   { 
     _desciplines.Clear();
     //код для загрузки
   }
}


public class StudentViewModel :  INotifyPropertyChanged
{
   private _model;
   private ICollectionView _desciplinesViewModel;


   public StudentViewModel()
   {
      _model = new Student();
      _desciplinesViewModel = new CollectionViewModel<Discipline>(_model.Disciplines);
   }

    // The View binds to the desciplines ViewModel through the ICollectionView interface.
        public ICollectionView Disciplines
        {
            get { return _desciplinesViewModel ; }
        }


       public LoadDisciplines()
       {
           IsBusy = true;
           _model.LoadDisciplines();
           IsBusy = false;
       }

CollectionViewModel поддерживает сортировку,группировку, фильтрацию

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2008-2009 David Hill. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
///////////////////////////////////////////////////////////////////////////////
using System;
using System.Reflection;
using System.Linq;
using System.Linq.Expressions;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Collections.ObjectModel;

using Microsoft.Practices.Composite.Presentation.Commands;

namespace Helix.Samples.CollectionViewModel
{
    /// <summary>
    /// A generic ViewModel that wraps an underlying collection model.
    /// </summary>
    /// <typeparam name="T">The type of the items in the underlying collection</typeparam>
    public class CollectionViewModel<T> : ObservableCollection<T>, ICollectionView where T : class
    {
        // The underlying model that we're providing a CollectionViewModel for.
        private IEnumerable<T> _model;

        public CollectionViewModel( IEnumerable<T> model )
        {
            // Set reference to the underlying model.
            _model = model;

            // If the underlying model supports it,
            // monitor the underlying model for changes.
            INotifyCollectionChanged incc = _model as INotifyCollectionChanged;
            if ( incc != null )
            {
                incc.CollectionChanged += new NotifyCollectionChangedEventHandler( OnModelChanged );
            }

            // Initialize the view.
            UpdateView();

            // Initialize the CollectionViewModel's built-in commands.
            SelectNextCommand     = new DelegateCommand<object>( SelectNext, CanSelectNext );
            SelectPreviousCommand = new DelegateCommand<object>( SelectPrevious, CanSelectPrevious );
            SortByCommand         = new DelegateCommand<string>( SortBy );
            GroupByCommand        = new DelegateCommand<string>( GroupBy );
        }

        private int _currentIndex = -1;
        private SortDescriptionCollection _sortDescriptions;
        private ObservableCollection<GroupDescription> _groupDescriptions;

        // This should really be Predicate<T> but the ICollectionView
        // interface defines the filter predicate with an object type parameter.
        private Predicate<object> _filter;

        #region ICollectionView Members

        public event EventHandler CurrentChanged;

        public event CurrentChangingEventHandler CurrentChanging;

        public bool Contains( object item )
        {
            return base.Contains( item as T );
        }

        public object CurrentItem
        {
            get { return _currentIndex == -1 ? null : this[ _currentIndex ]; }
        }

        public int CurrentPosition
        {
            get { return _currentIndex; }
        }

        public bool IsCurrentAfterLast
        {
            get { return _currentIndex >= this.Count; }
        }

        public bool IsCurrentBeforeFirst
        {
            get { return _currentIndex < 0; }
        }

        public bool IsEmpty
        {
            get { return this.Count == 0; }
        }

        public bool MoveCurrentTo( object item )
        {
            if ( this.Contains( item ) )
            {
                return UpdateCurrentIndex( this.IndexOf( item as T ) );
            }

            // If item is not in collection or is null, move to unselected state.
            return UpdateCurrentIndex( -1 );
        }

        public bool MoveCurrentToFirst()
        {
            return UpdateCurrentIndex( 0 );
        }

        public bool MoveCurrentToLast()
        {
            return UpdateCurrentIndex( this.Count - 1 );
        }

        public bool MoveCurrentToNext()
        {
            return UpdateCurrentIndex( _currentIndex + 1 );
        }

        public bool MoveCurrentToPosition( int position )
        {
            return UpdateCurrentIndex( position );
        }

        public bool MoveCurrentToPrevious()
        {
            return UpdateCurrentIndex( _currentIndex > 0 ? _currentIndex - 1 : _currentIndex );
        }

        public void Refresh()
        {
            UpdateCurrentIndex( -1 );
        }

        public IEnumerable SourceCollection
        {
            get { Debug.Assert( _model != null ); return _model as IEnumerable; }
        }

        public bool CanFilter
        {
            get { return true; }
        }

        public Predicate<object> Filter
        {
            get { return _filter; }
            set { _filter = value; UpdateView(); }
        }

        public bool CanSort
        {
            get { return true; }
        }

        public SortDescriptionCollection SortDescriptions
        {
            get
            {
                if ( _sortDescriptions == null )
                {
                    _sortDescriptions = new SortDescriptionCollection();

                    // Monitor sort description collection for any changes.
                    ((INotifyCollectionChanged)_sortDescriptions).CollectionChanged += ( sender, e ) => UpdateView();
                }
                return _sortDescriptions;
            }
        }

        public bool CanGroup
        {
            get { return true; }
        }

        public ObservableCollection<GroupDescription> GroupDescriptions
        {
            get
            {
                if ( _groupDescriptions == null )
                {
                    _groupDescriptions = new ObservableCollection<GroupDescription>();

                    // Monitor group description collection for any changes.
                    _groupDescriptions.CollectionChanged += ( sender, e ) => UpdateView();
                }
                return _groupDescriptions;
            }
        }

        public ReadOnlyObservableCollection<object> Groups
        {
            get { throw new NotImplementedException(); }
        }

        public IDisposable DeferRefresh()
        {
            throw new NotImplementedException();
        }

        public System.Globalization.CultureInfo Culture
        {
            get
            {
                return System.Globalization.CultureInfo.InvariantCulture;
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        #endregion

        #region Commands - SelectNext, SelectPrevious, SortBy, GroupBy
        public DelegateCommand<object> SelectNextCommand { get; private set; }

        private bool CanSelectNext( object arg )
        {
            return ( _currentIndex < this.Count - 1 );
        }

        private void SelectNext( object obj )
        {
            MoveCurrentToNext();
        }

        public DelegateCommand<object> SelectPreviousCommand { get; private set; }
        private bool CanSelectPrevious( object arg )
        {
            return ( _currentIndex > 0 );
        }

        private void SelectPrevious( object obj )
        {
            MoveCurrentToPrevious();
        }

        public DelegateCommand<string> SortByCommand { get; private set; }

        private void SortBy( string property )
        {
            this.SortDescriptions.Clear();
            SortDescriptions.Add( new SortDescription( property, ListSortDirection.Ascending ) );
        }

        public DelegateCommand<string> GroupByCommand { get; private set; }

        private void GroupBy( string property )
        {
            this.GroupDescriptions.Clear();
            GroupDescriptions.Add( new PropertyGroupDescription( property ) );
        }
        #endregion

        private void UpdateView()
        {
            IQueryable<T> results = BuildQuery();

            base.Clear();

            foreach ( T item in results )
            {
                this.Add( item );
            }

            UpdateCurrentIndex( -1 );
        }

        private IQueryable<T> BuildQuery()
        {
            // Build up a dynamic query using Linq Expressions.
            IQueryable<T> query = _model.AsQueryable<T>();

            Expression viewExpression = query.Expression;

            // Start with the filter expression.
            if ( Filter != null )
            {
                Expression<Func<T, bool>> filterLambda = item => Filter( item );

                viewExpression = Expression.Call(
                    typeof( Queryable ),
                    "Where",
                    new Type[] { query.ElementType },   // TSource.
                    viewExpression,
                    filterLambda );
            }

            // Append the sorting expression(s).
            string sortMethodName = "OrderBy";
            foreach( SortDescription sort in SortDescriptions )
            {
                // Create local parameter for lambda - know your closures!
                string propertyName = sort.PropertyName;
                Expression<Func<T, object>> sortLambda = item => GetPropertyValue( item, propertyName );

                viewExpression = Expression.Call(
                    typeof( Queryable ),
                    sortMethodName + (sort.Direction == ListSortDirection.Ascending ? "" : "Descending"),
                    new Type[] { query.ElementType, typeof( object ) }, // TSource, TKey
                    viewExpression,
                    sortLambda );

                // Switch to ThenBy for subsequent sorting.
                sortMethodName = "ThenBy";
            }

            // Append the grouping expression(s).
            // TODO: Support nested grouping properly...
            foreach ( GroupDescription group in GroupDescriptions )
            {
                PropertyGroupDescription propGroup = group as PropertyGroupDescription;
                if (propGroup != null)
                {
                    // Create local parameter for lambda - know your closures!
                    string propertyName = propGroup.PropertyName;
                    Expression<Func<T, object>> groupLambda = item => GetPropertyValue( item, propertyName );

                    viewExpression = Expression.Call(
                    typeof( Queryable ),
                    "GroupBy",
                    new Type[] { query.ElementType, typeof( object ) }, // TSource, TKey
                    viewExpression,
                    groupLambda );

                    // Sort by the grouping key.
                    Expression<Func<IGrouping<object,T>, object>> sortLambda = g => g.Key;

                    viewExpression = Expression.Call(
                    typeof( Queryable ),
                    "OrderBy",
                    new Type[] { typeof(IGrouping<object,T>), typeof( object ) }, // TSource, TKey
                    viewExpression,
                    sortLambda );

                    // Flatten the groups using SelectMany.
                    Expression<Func<IGrouping<object, T>, IEnumerable<T>>> smLambda = g => g;

                    viewExpression = Expression.Call(
                    typeof( Queryable ),
                    "SelectMany",
                    new Type[] { typeof( IGrouping<object, T> ), query.ElementType }, // TSource, TResult
                    viewExpression,
                    smLambda );
                }
            }

            // Return the query.
            return query.Provider.CreateQuery<T>( viewExpression ); ;
        }

        private void OnModelChanged( object sender, NotifyCollectionChangedEventArgs args )
        {
            switch ( args.Action )
            {
                case NotifyCollectionChangedAction.Remove:
                    // If the removed item is in the current view, just remove it.
                    foreach ( T item in args.OldItems )
                    {
                        if ( base.Contains( item ) )
                        {
                            this.Remove( item );
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Add:
                case NotifyCollectionChangedAction.Replace:
                case NotifyCollectionChangedAction.Reset:
                    // Could optimize here, but just update the whole view for now...
                    UpdateView();
                    break;
            }
        }

        private object GetPropertyValue( T item, string propertyName )
        {
            PropertyInfo pi = item.GetType().GetProperty( propertyName );
            if ( pi != null )
            {
                return pi.GetValue( item, null );
            }
            return null;
        }

        private bool UpdateCurrentIndex( int index )
        {
            // Calculate new index bounded by -1 and the current collection size.
            int newIndex;
            newIndex = System.Math.Max( index, -1 );
            newIndex = System.Math.Min( newIndex, this.Count - 1 );

            if ( _currentIndex != newIndex )
            {
                if ( this.CurrentChanging != null )
                {
                    this.CurrentChanging( this, new CurrentChangingEventArgs( false ) );
                }

                _currentIndex = newIndex;

                if ( this.CurrentChanged != null )
                {
                    this.CurrentChanged( this, new EventArgs() );
                }

                OnPropertyChanged( new PropertyChangedEventArgs( "CurrentPosition" ) );
                OnPropertyChanged( new PropertyChangedEventArgs( "CurrentItem" ) );
                SelectNextCommand.RaiseCanExecuteChanged();
                SelectPreviousCommand.RaiseCanExecuteChanged();
            }

            return _currentIndex != -1;
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2008-2009 David Hill. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
///////////////////////////////////////////////////////////////////////////////
using System;
using System.ComponentModel;
using System.Reflection;

namespace Helix.Samples.CollectionViewModel
{
    public class PropertyGroupDescription : GroupDescription
    {
        public string PropertyName { get; private set; }

        public PropertyGroupDescription( string propertyName )
        {
            PropertyName = propertyName;
        }

        public override object GroupNameFromItem( object item, int level, System.Globalization.CultureInfo culture )
        {
            PropertyInfo pi = item.GetType().GetProperty( PropertyName );
            if ( pi != null )
            {
                return pi.GetValue( item, null );
            }
            return null;
        }
    }
}

29 июн 09, 16:38    [7355552]     Ответить | Цитировать Сообщить модератору
 Re: Сортировка в Entity framework  [new]
GraDea
Member

Откуда: Москва
Сообщений: 52
Почти все заработало. Просто не там где надо указывал байдинг для датаконтекста, указывал в гриде, а надо было в Window.

Но вернулась старая проблема - единоразовая сортировка. При клике по студенту первый раз - все датагриды заполняются отсортировано, второй и последущие клики на других и этом студенте выводят несортированные данные (можно руками кликнуть по заголовку и отсортировать).

И я даже не знаю куда копать... Почему то, что работает один раз, не работает второй %) ???
30 июн 09, 14:52    [7359360]     Ответить | Цитировать Сообщить модератору
Все форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM Ответить