Реализация IPagedList <T> на моих моделях с помощью NHibernate

Я создал его, используя свойство Translate:

Без жесткого кодирования вам придется использовать JavaScript с большим количеством вычислений

[110 ]
   <nav>
    <ul>
      <li><a href="#">Home</a></li>
      <li><a href="#product-test">Products</a></li>
      <li><a href="#quote">Quota</a></li>
      <li><a href="#about">About</a></li>
      <li><a href="contact.html">Contact</a></li>
      <div class="line"></div>
    </ul>
    </div>
  </nav>

6
задан John_ 22 May 2009 в 06:55
поделиться

3 ответа

Хорошо, я отправлю это как ответ, потому что он делает в основном то, что я хотел. Однако я хотел бы получить обратную связь, а также, возможно, ответ на мое единственное предостережение относительно решения:

Я создал интерфейс под названием IPagedList.

public interface IPagedList<T> : IList<T>, ICollection
{

    IList<T> GetPagedList(int pageNo, int pageSize);

}

Затем создал базовый класс, который он наследует от IPagedList:

public class PagedList<T> : IPagedList<T>
{

    private List<T> _collection = new List<T>();

    public IList<T> GetPagedList(int pageNo, int pageSize)
    {
        return _collection.Take(pageSize).Skip((pageNo - 1) * pageSize).ToList();
    }

    public int IndexOf(T item)
    {
        return _collection.IndexOf(item);
    }

    public void Insert(int index, T item)
    {
        _collection.Insert(index, item);
    }

    public void RemoveAt(int index)
    {
        _collection.RemoveAt(index);
    }

    public T this[int index]
    {
        get
        {
            return _collection[index];
        }
        set
        {
            _collection[index] = value;
        }
    }

    public void Add(T item)
    {
        _collection.Add(item);
    }

    public void Clear()
    {
        _collection.Clear();
    }

    public bool Contains(T item)
    {
        return _collection.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        _collection.CopyTo(array, arrayIndex);
    }

    int Count
    {
        get
        {
            return _collection.Count;
        }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(T item)
    {
        return _collection.Remove(item);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _collection.GetEnumerator();
    }

    int ICollection<T>.Count
    {
        get { return _collection.Count; }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _collection.GetEnumerator();
    }

    public void CopyTo(Array array, int index)
    {
        T[] arr = new T[array.Length];
        for (int i = 0; i < array.Length ; i++)
        {
            arr[i] = (T)array.GetValue(i);
        }

        _collection.CopyTo(arr, index);
    }

    int ICollection.Count
    {
        get { return _collection.Count; }
    }

    // The IsSynchronized Boolean property returns True if the 
    // collection is designed to be thread safe; otherwise, it returns False.
    public bool IsSynchronized
    {
        get 
        {
            return false;
        }
    }

    public object SyncRoot
    {
        get 
        {
            return this;
        }
    }
}

Затем я создаю IUserCollectionType для NHibernate для использования в качестве настраиваемого типа коллекции и NHPagedList, который наследуется от PersistentGenericBag, IPagedList в качестве фактической коллекции. Я создал для них два отдельных класса, потому что казалось, что использование IUserCollectionType вообще не повлияло на фактическую коллекцию, которая будет использоваться, поэтому я сохранил две части логики отдельно. Код ниже для обоих из вышеперечисленных:

public class PagedListFactory<T> : IUserCollectionType
{

    public PagedListFactory()
    { }

    #region IUserCollectionType Members

    public bool Contains(object collection, object entity)
    {
        return ((IList<T>)collection).Contains((T)entity);
    }

    public IEnumerable GetElements(object collection)
    {
        return (IEnumerable)collection;
    }

    public object IndexOf(object collection, object entity)
    {
        return ((IList<T>)collection).IndexOf((T)entity);
    }

    public object Instantiate(int anticipatedSize)
    {
        return new PagedList<T>();
    }

    public IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister)
    {
        return new NHPagedList<T>(session);
    }

    public object ReplaceElements(object original, object target, ICollectionPersister persister, 
            object owner, IDictionary copyCache, ISessionImplementor session)
    {
        IList<T> result = (IList<T>)target;

        result.Clear();
        foreach (object item in ((IEnumerable)original))
        {
            result.Add((T)item);
        }

        return result;
    }

    public IPersistentCollection Wrap(ISessionImplementor session, object collection)
    {
        return new NHPagedList<T>(session, (IList<T>)collection);
    }

    #endregion
}

NHPagedList следующий:

public class NHPagedList<T> : PersistentGenericBag<T>, IPagedList<T>
{

    public NHPagedList(ISessionImplementor session) : base(session)
    {
        _sessionImplementor = session;
    }

    public NHPagedList(ISessionImplementor session, IList<T> collection)
        : base(session, collection)
    {
        _sessionImplementor = session;
    }

    private ICollectionPersister _collectionPersister = null;
    public NHPagedList<T> CollectionPersister(ICollectionPersister collectionPersister)
    {
        _collectionPersister = collectionPersister;
        return this;
    }

    protected ISessionImplementor _sessionImplementor = null;

    public virtual IList<T> GetPagedList(int pageNo, int pageSize)
    {
        if (!this.WasInitialized)
        {
            IQuery pagedList = _sessionImplementor
                .GetSession()
                .CreateFilter(this, "")
                .SetMaxResults(pageSize)
                .SetFirstResult((pageNo - 1) * pageSize);

            return pagedList.List<T>();
        }

        return this
                .Skip((pageNo - 1) * pageSize)
                .Take(pageSize)
                .ToList<T>();
    }

    public new int Count
    {
        get
        {
            if (!this.WasInitialized)
            {
                return Convert.ToInt32(_sessionImplementor.GetSession().CreateFilter(this, "select count(*)").List()[0].ToString());
            }

            return base.Count;
        }
    }

}

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

Теперь вы готовы к работе, просто измените текущие ссылки IList в ваших моделях на IPagedList, а затем сопоставьте NHibernate с новой настраиваемой коллекцией, используя беглый NHibernate, как показано ниже, и вы готовы к работе.

.CollectionType<PagedListFactory<Recipient>>()

Это первая итерация этого кода, поэтому для его совершенства потребуется некоторый рефакторинг и модификации.

Моя единственная проблема на данный момент заключается в том, что он не получит выгружаемые элементы в том порядке, который предлагает файл сопоставления для отношения между родителями и детьми. Я добавил на карту атрибут упорядочивания, и он просто не обращает на него внимания. Где, как и любые другие предложения where, в каждом запросе нет проблем. Кто-нибудь знает, почему это может происходить и есть ли что-то вокруг? Я буду разочарован, если не смогу решить эту проблему.

2
ответ дан 17 December 2019 в 07:08
поделиться

Если вы собираетесь сделать что-то подобное, я не могу придумать способа, как вы могли бы «писать» в выгружаемую коллекцию, чтобы NH оставалась. Страничная коллекция будет доступна только для чтения.

Если это нормально, вы можете использовать такой подход: http://www.acceptedeclectic.com/2007/12/generic-custom-nhibernate-collections.html

Он упаковывает PersistentGenericBag и добавляет несколько дополнительных методов, как вы описали. Затем GetPagedList () может быть реализован с критериями, которые возвращают ReadOnlyCollection, как и Count - конечно, возвращая long. Метод GetAll () не понадобится, он просто сам вернет коллекцию, насколько я могу судить.

Что касается хорошей идеи, я думаю, что да, если у вас много коллекций, где это актуальная проблема.

0
ответ дан 17 December 2019 в 07:08
поделиться

Вам следует обратиться к одному из поставщиков LINQ для NHibernate. Вы ищете способ отложить загрузку результатов по вашему запросу. Самая большая сила LINQ в том, что он делает именно это ... задерживает загрузку результатов ваших запросов. Когда вы фактически создаете запрос, на самом деле создается дерево выражения, которое представляет то, что вы хотите сделать, чтобы это можно было сделать позже. Используя поставщик LINQ для NHibernate, вы сможете сделать что-то вроде следующего:

public abstract class Repository<T> where T: class
{
    public abstract T GetByID(int id);
    public abstract IQueryable<T> GetAll();
    public abstract T Insert(T entity);
    public abstract void Update(T entity);
    public abstract void Delete(T entity);
}

public class RecipientRepository: Repository<Recipient>;
{
    // ...

    public override IQueryable<Recipient> GetAll()
    {
        using (ISession session = /* get session */)
        {
            // Gets a query that will return all Recipient entities if iterated
            IQueryable<Recipient> query = session.Linq<Recipient>();
            return query;
        }
    }

    // ...
}

public class RecipientList
{
    public IQueryable<Recipient> Recipients
    {
        RecipientRepository repository = new RecipientRepository();
        return repository.GetAll(); // Returns a query, does not evaluate, so does not hit database
    }
}

// Consuming RecipientList in some higher level service, you can now do:    
public class RecipientService
{
    public IList<Recipient> GetPagedList(int page, int size)
    {
        RecipientList list = // get instance of RecipientList
        IQueryable<Recipient> query = list.Recipients.Skip(page*size).Take(size); // Get your page
        IList<Recipient> listOfRecipients = query.ToList(); // <-- Evaluation happens here!
        reutrn listOfRecipients;
    }
}

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

Эта возможность может пойти гораздо дальше, чем запрос одной страницы результатов. Методы расширения .Skip () и .Take () доступны для всех объектов IQueryable и IEnumerable , а также для целого ряда других. Кроме того, у вас есть .Where (), .Except (), .Join () и многие, многие другие. Это дает вам возможность, скажем, .GetAll (), а затем отфильтровать возможные результаты этого запроса одним или несколькими вызовами .Where (), заканчивая .Skip (...). Take (...) , заканчивая одной оценкой в ​​вашем вызове .ToList () (или .ToArray ()).

Это потребует, чтобы вы немного изменили свой домен и начали передавать IQueryable или IEnumerable вместо IList , и конвертировать в IList только на ваших «общедоступных» сервисах более высокого уровня.

2
ответ дан 17 December 2019 в 07:08
поделиться
Другие вопросы по тегам:

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