代码之家  ›  专栏  ›  技术社区  ›  John_

使用NHibernate在我的模型上实现IPagedList<T>

  •  6
  • John_  · 技术社区  · 16 年前

    例如

    RecipientList。收件人将返回列表中的每个收件人。

    我想实现一种方法,将所有oen上的分页添加到模型中的许多关系中,最好使用一个接口,但实际上任何不会将类型化关系强制到模型上的东西。例如,最好有以下界面:

    public interface IPagedList<T> : IList<T>
    {
        int Count { get; }
        IList<T> GetPagedList(int pageNo, int pageSize);
        IList<T> GetAll();
    }
    

    IList<Recipient> recipients = RecipientList.Recipients.GetPagedList(1, 400);
    

    我一直在想办法做到这一点,但没有给模型任何分页的意识,但我的头撞到了砖墙的时刻。

    我是否可以用NHibernate目前用于IList和lazyloading的类似方式实现该接口?我对NHibernate的了解还不够。

    实现这一点是个好主意吗?作为本公司唯一的.NET开发人员,您的想法将不胜感激,因为我没有人能提出您的想法。

    使现代化

    下面的帖子向我指出了NHibernate的自定义集合属性,这将很好地工作。但是,我不确定解决这个问题的最佳方法是什么,我尝试从PersistentGenericBag继承,这样它就具有与IList相同的基本功能,而无需做太多工作,但是我不确定如何根据ISessionImplementor收集对象列表。我需要知道如何:

    • 获取我将要填充的当前IList的一些ICriteria详细信息
    • 获取与IList关联的特定属性的映射详细信息,以便我可以创建自己的ICriteria。

    谢谢

    3 回复  |  直到 16 年前
        1
  •  2
  •   John_    16 年前

    好的,我将把这个作为一个答案发布,因为它主要是做我想要的。然而,我希望得到一些反馈,也可能是我迄今为止对解决方案的一个警告的答案:

    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;
            }
        }
    }
    

    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,然后使用下面的fluent NHibernate将NHibernate映射到新的自定义集合,就可以开始了。

    .CollectionType<PagedListFactory<Recipient>>()
    

    这是这段代码的第一个版本,因此需要进行一些重构和修改才能使其完美。

        2
  •  2
  •   jrista    16 年前

    您应该查看NHibernate的一个LINQ提供者。您需要的是一种延迟加载查询结果的方法。LINQ最大的功能就是它确实可以…延迟加载查询结果。当您实际构建一个查询时,实际上是在创建一个表达式树来表示您想要做的事情,以便以后可以实际执行。通过使用NHibernate的LINQ提供程序,您可以执行以下操作:

    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上都可用<T>和IEnumerable<T>对象,以及一大堆其他对象。此外,您还有.Where()、.Except()、.Join()以及更多。这使您能够,例如,.GetAll(),然后通过对.Where()的一个或多个调用过滤该查询的可能结果,以.Skip(…).Take(…)结束,在.ToList()或.ToArray()调用中以单个计算结束。

    这需要您稍微更改您的域,并开始通过IQueryable<T>或IEnumerable<T>在IList的周围<T>,并且仅转换为IList<T>在您更高级别的“面向公众”服务中。

        3
  •  0
  •   asgerhallas    16 年前

    如果你要做这样的事情,我想不出一个办法,你可以“写”到页面集合,让NH持久化。分页的集合将是只读的。

    http://www.acceptedeclectic.com/2007/12/generic-custom-nhibernate-collections.html

    他正在包装PersistentGenericBag并添加一些ekstra方法,正如您所描述的。GetPagedList()可以使用一个条件来实现,该条件返回一个ReadOnlyCollection,也可以计数-当然返回一个long。GetAll()方法不是必需的,就我所见,它只是返回集合本身。

    至于这是否是一个好主意,我确实认为是,如果你有很多收藏,这是一个实际问题。如果只是一个或两个collection,我会选择在实体上有一个方法,返回页面中的集合。