This question is mostly not a problem help call, but an invitation for the comparison between developers on contemporary, more advanced and clean development methods.
Here I explain how I've resolved this problem and show you a working code example. Anyway, I have some doubts about my solution. I'm really curios if into nature exists a more elegant way to achieve the same code optimization.
I've started from the issue when I had two different controllers with mostly same response models except Items property type.
Precision: We need to have dedicated and not shared response models for each controller. In my opinion it helps in the future, when the only one controller's response have to be changed without creating side effects for the others.
I've started from the problem when I had two different controllers with mostly same response models except Items property type.
Here them are:
namespace Webapi.Models.File {
public class Response {
public FileItem [] Items { get; set; }
public int Page { get; set; }
public int TotalPages { get; set; }
}
public class FileItem {
...
}
}
namespace Webapi.Models.User {
public class Response {
public UserItem [] Items { get; set; }
public int Page { get; set; }
public int TotalPages { get; set; }
}
public class UserItem {
...
}
}
The first model is populated in this way:
using FileModel = Webapi.Models.File;
private FileModel.Response CreateItemsPage(List<FileModel.FileItem> items, int page) {
int maxItemsPerPage = 50;
var chunks = items.Select((v, i) => new { Value = v, Index = i })
.GroupBy(x => x.Index / maxItemsPerPage).Select(grp => grp.Select(x => x.Value));
int totalChunks = chunks.Count();
if(totalChunks == 0) {
return null;
}
page = page > 1 ? page : 1;
page = totalChunks < page ? 1 : page;
return new FileModel.Response() {
Items = (chunks.ToArray())[page-1].ToArray(),
Page = page,
TotalPages = totalChunks
};
}
And the second method is exactly the same except input (List<UserModel.UserItem>) and output (UserModel.Response) types:
using UserModel = Webapi.Models.User;
private UserModel.Response CreateItemsPage(List<UserModel.UserItem> items, int page) {
int maxItemsPerPage = 50;
var chunks = items.Select((v, i) => new { Value = v, Index = i })
.GroupBy(x => x.Index / maxItemsPerPage).Select(grp => grp.Select(x => x.Value));
int totalChunks = chunks.Count();
if(totalChunks == 0) {
return null;
}
page = page > 1 ? page : 1;
page = totalChunks < page ? 1 : page;
return new UserModel.Response() {
Items = (chunks.ToArray())[page-1].ToArray(),
Page = page,
TotalPages = totalChunks
};
}
Having two and then even more cloned methods inside my webapi's controllers isn't a good perspective and I have resolved this by creating two ObjectExtensions methods.
The first one just reassign properties from source to target object. Both logically must have same properties inside (name and type):
public static TTarget AssignProperties<TTarget, TSource>(this TTarget target, TSource source) {
foreach (var targetProp in target.GetType().GetProperties()) {
foreach (var sourceProp in source.GetType().GetProperties()) {
if (targetProp.Name == sourceProp.Name && targetProp.PropertyType == sourceProp.PropertyType) {
targetProp.SetValue(target, sourceProp.GetValue(source));
break;
}
}
}
return target;
}
The second receives target and source objects, creates internally an anonymous one, then reassigns properties from it by using the previous extension method AssignProperties to the target object (need this because cannot access generic objects properties directly):
public static TTarget CreateItemsPage<TTarget, TSource>(this TTarget target, List<TSource> items, int page = 1) {
int maxItemsPerPage = 50;
var chunks = items.Select((v, i) => new { Value = v, Index = i })
.GroupBy(x => x.Index / maxItemsPerPage).Select(grp => grp.Select(x => x.Value));
int totalChunks = chunks.Count();
if(totalChunks == 0) {
return target;
}
page = page > 1 ? page : 1;
page = totalChunks < page ? 1 : page;
var source = new {
Items = (chunks.ToArray())[page-1].ToArray(),
Page = page,
TotalPages = totalChunks
};
target = target.AssignProperties(source);
return target;
}
And here is the usage:
...
var items = _filesService.ListAllUserFiles(userId, requestData.SearchText);
if(pages.Count() == 0)
return BadRequest();
return Ok(new FileModel.Response().CreateItemsPage(items, requestData.Page));
...
Some code examples would be appreciated. Thank you!
By opening a discussion on Reddit I came to the following solution that allows me to keep models separate and remove the inefficient AssignProperties method.
Interface:
public interface IPaginationResponse<TItem> {
TItem[] Items { get; set; }
int Page { get; set; }
int TotalPages { get; set; }
}
Models examples:
public class Response: IPaginationResponse<Info> {
public Info [] Items { get; set; }
public int Page { get; set; }
public int TotalPages { get; set; }
...
}
public class Response: IPaginationResponse<UserFile> {
public UserFile [] Items { get; set; }
public int Page { get; set; }
public int TotalPages { get; set; }
...
}
public class Response: IPaginationResponse<UserItem> {
public UserItem [] Items { get; set; }
public int Page { get; set; }
public int TotalPages { get; set; }
...
}
Now finally I've removed AssignProperties from CreateItemsPage extension method. Thanks to where TTarget : IPaginationResponse<TSource> I can assign values directly to TTarget target
public static TTarget CreateItemsPage<TTarget, TSource>(this TTarget target, IEnumerable<TSource> items, int page = 1) where TTarget : IPaginationResponse<TSource> {
...
target.Items = (chunks.ToArray())[page-1].ToArray();
target.Page = page;
target.TotalPages = totalChunks;
return target;
}
Inside controller I invoke it in the same way
return Ok(new FileModel.Response().CreateItemsPage(pages, requestData.Page));
As both Response classes are different in Item type only, a single generic type could be used instead of two non-generic types.
public class ResponsePageTemplate<TItem>
{
public TItem[] Items { get; set; }
public int Page { get; set; }
public int TotalPages { get; set; }
}
Then extension method would be like:
public static ResponsePageTemplate<TSource> CreateItemsPage<TSource>(this IEnumerable<TSource> items, int page = 1)
{
int maxItemsPerPage = 50;
var chunks = items.Select((v, i) => new {Value = v, Index = i})
.GroupBy(x => x.Index / maxItemsPerPage).Select(grp => grp.Select(x => x.Value));
int totalChunks = chunks.Count();
if (totalChunks == 0)
{
return new ResponsePageTemplate<TSource>();
}
page = page > 1 ? page : 1;
page = totalChunks < page ? 1 : page;
var result = new ResponsePageTemplate<TSource>
{
Items = (chunks.ToArray())[page - 1].ToArray(),
Page = page,
TotalPages = totalChunks
};
return result;
}
And usage would be like:
return Ok(items.CreateItemsPage(requestData.Page));
Related
Is there a way to implement Generic filter for NHibernate on a repository layer? As per Microsoft API Guidelines, the sort and filters are passed as string API Guidelines. what will be the approach to that using NHibernate to make it generic?
Please have a look at this post.There is a section Paging with NHibernate.
The code snippet below is from this post.
The approach is to implement a class (in the post it was PagedResult<T>) in your case FilteredResult<T>. Having this class you can implementan extension (in post was NHibernatePagedResultExtensions) in this case NHibernateFilteredResultExtensions.
And the last thing left is to apply this extansion exacly the same way.
Hope this will help.
public abstract class PagedResultBase
{
public int CurrentPage { get; set; }
public int PageCount { get; set; }
public int PageSize { get; set; }
public int RowCount { get; set; }
public string LinkTemplate { get; set; }
public int FirstRowOnPage
{
get { return (CurrentPage - 1) * PageSize + 1; }
}
public int LastRowOnPage
{
get { return Math.Min(CurrentPage * PageSize, RowCount); }
}
}
public class PagedResult<T> : PagedResultBase
{
public IList<T> Results { get; set; }
public PagedResult()
{
Results = new List<T>();
}
}
public static class NHibernatePagedResultExtensions
{
public static async Task<PagedResult<T>> GetPagedAsync<T>(this IQueryable<T> query, int page, int pageSize)
{
var result = new PagedResult<T>
{
CurrentPage = page,
PageSize = pageSize,
RowCount = query.Count()
};
var pageCount = (double)result.RowCount / pageSize;
result.PageCount = (int)Math.Ceiling(pageCount);
var skip = (page - 1) * pageSize;
result.Results = await query.Skip(skip).Take(pageSize).ToListAsync();
return result;
}
}
public async Task<IActionResult> Index(int page = 1)
{
var result = await _session.RunInTransaction(async () =>
{
var books = await _session.Books
.Where(b => b.Title.StartsWith("How to"))
.GetPagedAsync(page, pageSize: 25);
return _mapper.Map<PagedResult<BookModel>>(books);
});
return View(result);
}
i have table looks like below
ID | Reason | PrID
-----------------
1 abc null
2 dhe null
3 aerc 1
4 dwes 2
5 adfje 1
i have class
public class Reason
{
public int Id { get; set; }
public string Reson{ get; set; }
public List<SecondryReason> SecReason{ get; set; }
public int? PrimaryId { get; set; }
}
public class SecondryReason
{
public int Id { get; set; }
public string Reason { get; set; }
public int PrimaryReasonId { get; set; }
}
I want this to be displayed in hierarchy level
if the prid is Null need to treat this as the parent remaining all child
i am trying Linq and unable to achieve this
Suggest me how to do this in an easy way in linq
So: You have a list/enumerable of type , whereof the SecReason List property is null. Then, using linq you want a list, were the only the "root" reasons remain, but the Sub-reasons got put in the lists, but as type SecondaryReason?
If so, I found this way to do it (linq and foreach):
static IEnumerable<Reason> GetReasonsGrouped(List<Reason> reasons)
{
var result = reasons.Where(x => x.PrimaryId == null);
foreach (var item in result)
{
item.SecReason = reasons.Where(x => x.PrimaryId == item.Id)
.Select(x => new SecondryReason()
{ Id = x.Id,
ReasonName = x.ReasonName,
PrimaryReasonId = item.Id
})
.ToList();
}
return result;
}
Or just linq, but harder to read:
var result = reasons.Where(x => x.PrimaryId == null)
.Select(x =>
{
x.SecReason = reasons.Where(r => x.PrimaryId == x.Id)
.Select(r => new SecondryReason()
{
Id = r.Id,
ReasonName = x.ReasonName,
PrimaryReasonId = x.Id
})
.ToList();
return x;
});
Not sure if linq will be the best solution, here is my proposed changes and method to get an Hierarchy type:
public class Reason
{
public int Id { get; set; }
public string Reson { get; set; }
public List<Reason> SecReason { get; set; }
public int? PrimaryId { get; set; }
//Adds child to this reason object or any of its children/grandchildren/... identified by primaryId
public bool addChild(int primaryId, Reason newChildNode)
{
if (Id.Equals(primaryId))
{
addChild(newChildNode);
return true;
}
else
{
if (SecReason != null)
{
foreach (Reason child in SecReason)
{
if (child.addChild(primaryId, newChildNode))
return true;
}
}
}
return false;
}
public void addChild(Reason child)
{
if (SecReason == null) SecReason = new List<Reason>();
SecReason.Add(child);
}
}
private List<Reason> GetReasonsHierarchy(List<Reason> reasons)
{
List<Reason> reasonsHierarchy = new List<Reason>();
foreach (Reason r in reasons)
{
bool parentFound = false;
if (r.PrimaryId != null)
{
foreach (Reason parent in reasonsHierarchy)
{
parentFound = parent.addChild(r.PrimaryId.Value, r);
if (parentFound) break;
}
}
if (!parentFound) reasonsHierarchy.Add(r);
}
return reasonsHierarchy;
}
What I'm trying to get is a property from an entity and sum the values. I'm using generics and the type of the entity and the name of the property are known at run time. The following (simplified) code compiles fine but throws "Object does not match target type." when trying to get the property from the entity. Here is the code:
public class SomeModel
{
public int ValueA { get; set; }
public int ValueB { get; set; }
}
public class SomeViewModel
{
public int ViewValue { get; set; }
}
public class HomeController : Controller
{
public ActionResult Index()
{
IEnumerable<SomeModel> data = GetData();
var result = SumValues(data, a => new SomeViewModel { ViewValue = a.ValueA });
return Content(string.Format("Total is: {0}", result));
}
private int SumValues<T, TRes>(IEnumerable<T> data, Func<T, TRes> transformation)
{
var transformedData = data.Select(transformation);
Type type = typeof(TRes);
PropertyInfo property = type.GetProperty("ViewValue");
IEnumerable<int> listOfValues = property.GetValue(transformedData, null) as IEnumerable<int>;
var sum = listOfValues.Sum();
return sum;
}
private IEnumerable<SomeModel> GetData()
{
var data = new List<SomeModel>();
Random random;
for (int i = 0; i < 1000; i++)
{
random = new Random(i);
data.Add(new SomeModel { ValueA = random.Next(5,100), ValueB = random.Next(20,80) });
}
return data;
}
}
I'm generating fake data here, but the real application is bringing data from database using EF. Generics is new to me so please any advice pointing me on the right direction will be very appreciated.
In this line:
IEnumerable<int> listOfValues =
property.GetValue(transformedData, null) as IEnumerable<int>;
You're trying to retrieve the value of the ViewValue property on a List<SomeModel> object, but List<SomeModel> doesn't have a ViewValue property. One thing you could do is this:
IEnumerable<int> listOfValues =
transformedData.Select(i => (int)property.GetValue(i, null));
But a much cleaner solution would be to avoid reflection entirely as it's not needed here:
interface IViewValue
{
int ViewValue { get; set; }
}
public class SomeViewModel : IViewValue
{
public int ViewValue { get; set; }
}
private int SumValues<T, TRes>(IEnumerable<T> data, Func<T, TRes> transformation)
where TRes : IViewValue
{
var transformedData = data.Select(transformation);
IEnumerable<int> listOfValues = transformedData.Select(i => i.ViewValue);
var sum = listOfValues.Sum();
return sum;
}
I have two objects of same type with different values:
public class Itemi
{
public Itemi()
{
}
public int Prop1Min { get; set; }
public int Prop1Max { get; set; }
public int Prop2Min { get; set; }
public int Prop2Max { get; set; }
public int Prop3Min { get; set; }
public int Prop3Max { get; set; }
...................................
public int Prop25Min { get; set; }
public int Prop25Max { get; set; }
}
Now I instantiate two objects of this type and add some values to their properties.
Itemi myItem1 = new Itemi();
myItem1.Prop1Min = 1;
myItem1.Prop1Max = 4;
myItem1.Prop2Min = 2;
myItem1.Prop2Max = 4;
myItem1.Prop3Min = -1;
myItem1.Prop3Max = 5;
.............................
myItem1.Prop25Min = 1;
myItem1.Prop25Max = 5;
Itemi myItem2 = new Itemi();
myItem2.Prop1Min = 1;
myItem2.Prop1Max = 5;
myItem2.Prop2Min = -10;
myItem2.Prop2Max = 3;
myItem2.Prop3Min = 0;
myItem2.Prop3Max = 2;
................................
myItem2.Prop25Min = 3;
myItem2.Prop25Max = 6;
What is the best and fastest way to do this comparison:
take each properties from myItem1 and check if values from Prop1-25 Min and Max are within the range values of myItem2 Prop1-25 Min and Max
Example:
myItem1.Prop1Min = 1
myItem1.Prop1Max = 4
myItem2.Prop1Min = 1
myItem2.Prop1Max = 5
this is True because mtItem1 Prop1 min and max are within the range of myItem2 min and max.
the condition should be AND in between all properties so in the end after we check all 25 properties if all of them are within the range of the second object we return true.
Is there a fast way to do this using Linq or other algorithm except the traditional if-else?
I would refactor the properties to be more along the lines of:
public class Item
{
public List<Range> Ranges { get; set; }
}
public class Range
{
public int Min { get; set; }
public int Max { get; set; }
}
Then your comparison method could be:
if (myItem1.Ranges.Count != myItem2.Ranges.Count)
{
return false;
}
for (int i = 0; i < myItem1.Ranges.Count; i++)
{
if (myItem1.Ranges[i].Min < myItem2.Ranges[i].Min ||
myItem1.Ranges[i].Max > myItem2.Ranges[i].Max)
{
return false;
}
}
return true;
Otherwise you will have to use Reflection, which is anything but fast.
Linq is using standart statements like if...then, for each and other, there is no magic :)
If the final goal only to compare, without needing to say, which properties are not in the range, then you not need to check them all, on the first unequals you can end checking.
Because you have so much properties, you must think about saving it in Dictionary, or List, for example. Or to use dynamic properties (ITypedList), if it will use for binding.
You really should do something like Ginosaji proposed.
But if you want to go with your current data model, here is how I would solve it. Happy typing. :)
public static bool RangeIsContained(int outerMin, int outerMax, int innerMin, int innerMax)
{
return (outerMin <= innerMin && outerMax >= innerMax);
}
public bool IsContained(Itemi outer, Itemi inner)
{
return RangeIsContained(outer.Prop1Min, outer.Prop1Max, inner.Prop1Min, inner.Prop1Max)
&& RangeIsContained(outer.Prop2Min, outer.Prop2Max, inner.Prop2Min, inner.Prop2Max)
// ...
&& RangeIsContained(outer.Prop25Min, outer.Prop25Max, inner.Prop25Min, inner.Prop25Max);
}
With your data model this is basically the only way to go except for reflection (slow!). LINQ cannot help you because your data is not enumerable.
For the sake of completeness, here is a LINQ solution (but it's less performant and less readable than Ginosaji's solution!)
public class Range
{
public int Min { get; set; }
public int Max { get; set; }
public static bool IsContained(Range super, Range sub)
{
return super.Min <= sub.Min
&& super.Max >= sub.Max;
}
}
public class Itemi
{
public Itemi()
{
properties = new Range[25];
for (int i = 0; i < properties.Length; i++)
{
properties[i] = new Range();
}
}
private Range[] properties;
public IEnumerable<Range> Properties { get { return properties; } }
public static bool IsContained(Itemi super, Itemi sub)
{
return super.properties
.Zip(sub.properties, (first, second) => Tuple.Create(first, second))
.All((entry) => Range.IsContained(entry.Item1, entry.Item2));
}
public Range Prop1
{
get { return properties[0]; }
set { properties[0] = value; }
}
public Range Prop2
{
get { return properties[1]; }
set { properties[1] = value; }
}
// ...
}
Using a paged result of some query i need to get from what page is a point.The object is return the data positioned at the right page when you push the point out of the scope opening the paged result at this page.
If the paged result can be obtain like this sample , how i can get from an item from what page is comming ?
paging
.skip(PAGE_SIZE * (PAGE_NUMBER - 1)).limit(PAGE_SIZE)
public List<BsonItem> GetData(QueryComplete query, int take, int skip, SortByBuilder sort)
{
var cursor = Db.Data.FindAs<BsonItem>(query);
if (skip > 0)
cursor.SetSkip(skip);
if (take > 0)
cursor.SetLimit(take);
if (sort != null )
cursor.SetSortOrder(sort);
return cursor.ToList();
}
Thanks for your help.
Page number should( and possible sort order and direction) come from the client side. So client click on the some page and than..
Personally i using some kind of filter that contains all properties that you need.
Using that filter you just need to specify page that you need to display in grid and how much items you need per page.
var pageNumber = 1;// current page should come from the client
var filter = new BaseFilter(){CurrentPage = pageNumber, ItemsPerPage = 30};
var items = GetItemsByFilter(filter, Query.LTE("SomeDate",DateTime.Now)),
SortBy.Ascending("SortField"));
//For basic paging you only following three properties
var totalCount = filter.TotalCount; // here will be total items count
var pagesCount = filter.TotalPagesCount; // here will be total pages count
// pageNumber = current page
Also you can inferit from BasicFilter and add any properties that you need for quering, sorting.
Here filter code(hope it will be useful for you):
public List<Item> GetItemsByFilter(BaseFilter filter,
QueryComplete query, SortByBuilder sort)
{
var resultItems = new List<Item>();
var cursor = Db.Data.FindAs<BsonItem>(query);
cursor.SetSortOrder(sort);
if (filter.IsNeedPaging)
{
cursor.SetSkip(filter.Skip).SetLimit(filter.Take);
filter.TotalCount = cursor.Count();
}
resultItems.AddRange(cursor);
return resultItems;
}
public class BaseFilter
{
private int _itemsPerPage = 10;
private int _skip = 0;
private int _currentPage = 1;
public BaseFilter()
{
IsNeedPaging = true;
}
public int Skip
{
get
{
if (_skip == 0)
_skip = (CurrentPage - 1) * _itemsPerPage;
return _skip;
}
set
{
_skip = value;
}
}
public int Take
{
get
{
return _itemsPerPage;
}
set
{
_itemsPerPage = value;
}
}
public bool IsNeedPaging { get; set; }
public int TotalCount { get; set; }
public int CurrentPage
{
get
{
return _currentPage;
}
set
{
_currentPage = value;
}
}
public int ItemsPerPage
{
get
{
return _itemsPerPage;
}
set
{
_itemsPerPage = value;
}
}
public int TotalPagesCount
{
get
{
return TotalCount / ItemsPerPage +
((TotalCount % ItemsPerPage > 0) ? 1 : 0);
}
}
}