如何设置一个存在于我的通用集合对象之上的通用分页对象?

时间:2014-12-19 00:01:16

标签: c# generics multiple-inheritance mixins castle-dynamicproxy

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

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

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

PagedCollection的示例:

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

1 个答案:

答案 0 :(得分:0)

使用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));