Reputation: 2052
I wrote a concrete class to expose database result set which includes T-type list items and total row count. After some reading(Dependency inversion principle, Interface segregation principle), I found that it should expose as Interface rather than concrete class.
Here is my code, what is the best way to write this class to expose as an IDbResultSet
rather exposing as DbResultSet
.
public class DbResultSet<T>
{
public List<T> Items { get; private set; }
public int TotalRowCount { get; private set; }
public DbResultSet(List<T> items, int totalRowCount)
{
this.Items = items;
this.TotalRowCount = totalRowCount;
}
}
Usage:
DbResultSet<Category> GetCategory(int categoryId);
Upvotes: 0
Views: 700
Reputation: 64933
There's no much you can do. It's just about designing the interface:
public interface IDbResultSet<T, TCollection>
where TCollection : ICollection<T>
{
TCollection Items { get; }
int TotalRowCount { get; }
}
As you can check above, the whole interface has no defined Items
collection type: it can be any collection which implements ICollection<T>
.
Now you may implement it as follows:
public class DbResultSet<T, TCollection> : IDbResultSet<T, TCollection>
where TCollection : ICollection<T>
{
public TCollection Items { get; private set; }
public int TotalRowCount { get; private set; }
public DbResultSet(TCollection items, int totalRowCount)
{
// Note that qualifying with "this" is redundant here
// so I've removed it
Items = items;
TotalRowCount = totalRowCount;
}
}
BTW, I would suggest you that you should change both the interface and class name from ResultSet
to Result
as is, because a set is a collection type on which there's no order but collection items are unique:
public interface IDbResult<T, TCollection>
where TCollection : ICollection<T>
{
TCollection Items { get; }
int TotalRowCount { get; }
}
public class DbResult<T, TCollection> : IDbResultSet<T, TCollection>
where TCollection : ICollection<T>
{
public TCollection Items { get; private set; }
public int TotalRowCount { get; private set; }
public DbResult(TCollection items, int totalRowCount)
{
// Note that qualifying with "this" is redundant here
// so I've removed it
Items = items;
TotalRowCount = totalRowCount;
}
}
Therefore, you may expose your results typed as interface types:
IDbResult<Category, IList<Category>> GetCategories();
I see that you want to use that approach even for a single result:
DbResultSet<Category> GetCategory(int categoryId);
My suggestion is that you should then split your results into two families:
So I would end up with these interfaces:
public interface IMultipleDbResult<T, TCollection>
where TCollection : ICollection<T>
{
TCollection Items { get; }
int TotalRowCount { get; }
}
public interface ISingleDbResult<T>
{
T Item { get; }
}
I'm suggesting you this because it's odd that you know that you're getting a category by Id
and you may need to get it from a list:
IDbResult<Category, IList<Category>> result = GetCategoryById(389);
Category category = result.Items[0]; // ??????
// vs.
ISingleDbResult<Category> result = GetCategoryById(389);
Category category = result.Item; // Isn't it more elegant?
BTW, I understand that your actual solution involves implementing more properties on both result types. For example to collect DB query error messages or other useful data.
In the other hand, I guess you're implementing a property like TotalRowCount
because you're paginating results. For example, there could be 300 categories but Items
property holds 10 items from the first page. Otherwise, your reasoning has some flaw, because all implementations of ICollection<T>
already own a Count
property.
Finally, perhaps be of interested on some design pattern I've published online. See Accmulated Result design pattern.
Upvotes: 1