如何设置位于通用集合对象之上的通用分页对象

本文关键字:对象 分页 置位 于通用 集合 | 更新日期: 2023-09-27 18:34:37

我一直在努力从我的应用程序中删除大量代码重复,特别是围绕我的模型。一些模型还具有一个集合变体,该变体是模型类型的 IEnumerable。以前,所有集合变体都是单独的实现,但我能够将它们的大部分代码合并到 ModelCollection 基类中。

现在,在这些集合模型之上是具有分页值的其他模型,它们都具有完全相同的属性,因此我也想将它们折叠到基类中。我遇到的问题是 .NET 不支持多重继承,并且因为每个 ModelCollection 实现仍然需要显式实现,因为大多数都有一些特殊的逻辑,一个简单的泛型链也无法解决问题。

模型集合基类:

public abstract class ModelCollection<TModel> : IEnumerable<T>, IModel where TModel : IPersistableModel
{
    protected readonly List<TModel> Models;
    protected ModelCollection()
    {
        Models = new List<TModel>();
    }
    protected ModelCollection(params TModel[] models)
        : this((IEnumerable<TModel>)models)
    {
    }
    protected ModelCollection(IEnumerable<TModel> models)
        : this()
    {
        models.ForEach(Models.Add);
    }
    public virtual int Count
    {
        get { return Models.Count; }
    }
    public virtual IEnumerator<TModel> GetEnumerator()
    {
        return Models.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    public virtual void Add(TModel model)
    {
        Models.Add(model);
    }
    public virtual void Accept(Breadcrumb breadcrumb, IModelVisitor visitor)
    {
        foreach (var model in this)
        {
            model.Accept(breadcrumb.Attach("Item"), visitor);
        }
        visitor.Visit(breadcrumb, this);
    }
    public bool IsSynchronized { get; set; }
}

分页集合的示例:

public class PagedNoteCollection : NoteCollection
{
    public PagedNoteCollection(params Note[] notes)
        : base(notes)
    {
    }
    public int CurrentPage { get; set; }
    public int TotalPages { get; set; }
    public int TotalNotesCount { get; set; }
}

供参考的 IModel 接口:

public interface IModel
{
    void Accept(Breadcrumb breadcrumb, IModelVisitor visitor);
}

如何设置位于通用集合对象之上的通用分页对象

这个问题可以使用Castle.DynamicProxy及其Mixins功能来解决。

首先,ModelCollection 基类需要有一个接口,在这个特定示例中,由于实现只是 IModel 和 IEnumerable 接口的组合,它只需要继承这些接口:

public interface IModelCollection<T> : IEnumerable<T>, IModel
{
}

其次,您将需要一个接口和一个实现来表示分页字段:

public interface IPagingDetails
{
    int CurrentPage { get; set; }
    int TotalPages { get; set; }
    int TotalCount { get; set; }
    bool HasPreviousPage { get; }
    int PreviousPage { get; }
    bool HasNextPage { get; }
    int NextPage { get; }
}
public class PagingDetails : IPagingDetails
{
    public int CurrentPage { get; set; }
    public int TotalPages { get; set; }
    public int TotalCount { get; set; }
    public bool HasPreviousPage
    {
        get { return CurrentPage > 1; }
    }
    public int PreviousPage
    {
        get { return CurrentPage - 1; }
    }
    public bool HasNextPage
    {
        get { return CurrentPage < TotalPages; }
    }
    public int NextPage
    {
        get { return CurrentPage + 1;}
    }
}

接下来,为了简化在生成代理后对各个部分的访问(特别是为了消除在 ModelCollection 和 PagingDetails 接口之间强制转换代理的要求(,我们需要一个空接口来组合分页和集合接口:

public interface IPagedCollection<T> : IPagingDetails, IModelCollection<T>
{}

最后,我们需要创建具有分页详细信息的代理作为 mixin:

public class PagedCollectionFactory
{
    public static IPagedCollection<TModel> GetAsPagedCollection<TModel, TCollection>(int currentPage, int totalPages, int totalCount, TCollection page)
        where TCollection : IModelCollection<TModel>
        where TModel : IPersistableModel
    {
        var generator = new ProxyGenerator();
        var options = new ProxyGenerationOptions();
        options.AddMixinInstance(new PagingDetails { CurrentPage = currentPage, TotalPages = totalPages, TotalCount = totalCount });
        return (IPagedCollection<TModel>)generator.CreateClassProxyWithTarget(typeof(TCollection), new[] { typeof(IPagedCollection<TModel>) }, page, options);
    }
}

现在我们可以使用代理(这取自我编写的单元测试以确保它正常工作(:

var collection = new NoteCollection(
            new Note {Text = "note 1"},
            new Note {Text = "note 2"},
            new Note {Text = "note 3"},
            new Note {Text = "note 4"});
var pagedCollection = PagedCollectionFactory.GetAsPagedCollection<Note,NoteCollection>(1, 1, 1, collection);
Assert.That(pagedCollection.CurrentPage, Is.EqualTo(1));
Assert.That(pagedCollection.ToList().Count, Is.EqualTo(4));