NHibernate projection, AutoMapping, IPagedList, how to? - nhibernate

I have these entities and models:
ENTITIES:
public class BlogPost {
public virtual int Id { get; set; }
public virtual IList<Keyword> Keywords { get; set; }
public virtual IList<BlogComment> Comments { get; set; }
}
public class BlogComment {
public virtual int Id { get; set; }
public virtual BlogPost Post { get; set; }
}
public class Keyword {
public virtual int Id { get; set; }
public virtual IList<BlogPost> BlogPosts { get; set; }
}
VIEW-MODELS:
public class BlogPostModel {
public int Id { get; set; }
// instead of IList<BlogComment> I have this:
public int CommentsCount { get; set; }
public IList<KeywordModel> Keywords { get; set; }
}
public class KeywordModel {
public int Id { get; set; }
public IList<BlogPostModel> Posts { get; set; }
}
public class BlogCommentModel {
public int Id { get; set; }
public BlogPostModel Post { get; set; }
}
Now I want to load a paged-list of blog-posts with their keywords and comments count and map them by AutoMapper library to a IPagedList<BlogPostModel>. Can you help me please? I'm using the Mvc IPagedList available at nuget:
public interface IPagedList<out T> : IPagedList, IEnumerable<T> {
T this[int index] { get; }
int Count { get; }
IPagedList GetMetaData();
}
public interface IPagedList {
int PageCount { get; }
int TotalItemCount { get; }
int PageNumber { get; }
int PageSize { get; }
bool HasPreviousPage { get; }
bool HasNextPage { get; }
bool IsFirstPage { get; }
bool IsLastPage { get; }
int FirstItemOnPage { get; }
int LastItemOnPage { get; }
}
I test many ways and googled the issue, but I can't find any solution. Thanks for any suggestion.

I had the same problem, and found the way how to fix it, probably this approach will help you too.
public class GenericModelViewConverter<TModel, TView> : IModelViewConverter<TModel, TView>
where TModel : class
where TView : class
{
/// <summary>
/// Initializes a new instance of the <see cref="GenericModelViewConverter{TModel, TView}"/> class.
/// </summary>
public GenericModelViewConverter()
{
// It is not he best place to create the mapping here
Mapper.CreateMap<TView, TModel>();
Mapper.CreateMap<TView, TModel>().ReverseMap();
}
/// <summary>
/// Converts the view to model.
/// </summary>
/// <param name="view">The view as a source.</param>
/// <param name="model">The model as a destination value.</param>
/// <returns>The destination model.</returns>
public virtual TModel ConvertViewToModel(TView view, TModel model = null)
{
return model == null ? Mapper.Map<TView, TModel>(view) : Mapper.Map(view, model);
}
/// <summary>
/// Converts the model to view.
/// </summary>
/// <param name="model">The model as a source.</param>
/// <param name="view">The view as a destination.</param>
/// <returns>The destination view.</returns>
public virtual TView ConvertModelToView(TModel model, TView view = null)
{
return view == null ? Mapper.Map<TModel, TView>(model) : Mapper.Map(model, view);
}
/// <summary>
/// Converts the view to model.
/// </summary>
/// <param name="viewCollection">The collection of views.</param>
/// <returns>The collection of models.</returns>
public virtual IEnumerable<TModel> ConvertViewToModel(IEnumerable<TView> viewCollection)
{
return Mapper.Map<IEnumerable<TView>, HashSet<TModel>>(viewCollection);
}
/// <summary>
/// Converts the model to view.
/// </summary>
/// <param name="modelCollection">The collection of models.</param>
/// <returns>The collection of views.</returns>
public virtual IEnumerable<TView> ConvertModelToView(IEnumerable<TModel> modelCollection)
{
return Mapper.Map<IEnumerable<TModel>, HashSet<TView>>(modelCollection);
}
/// <summary>
/// Converts the model to view.
/// </summary>
/// <param name="modelCollection">The model collection.</param>
/// <returns>The collection of models.</returns>
public virtual IPagedCollection<TView> ConvertModelToView(IPagedCollection<TModel> modelCollection)
{
var list = modelCollection.ToList();
var resultList = ConvertModelToView(list);
return new PagedCollection<TView>(resultList, modelCollection.PageIndex, modelCollection.PageSize, modelCollection.TotalItemCount);
}
/// <summary>
/// Converts the view to model.
/// </summary>
/// <param name="viewCollection">The view collection.</param>
/// <returns>The collection of views.</returns>
public virtual IPagedCollection<TModel> ConvertViewToModel(IPagedCollection<TView> viewCollection)
{
var list = viewCollection.ToList();
var resultList = ConvertViewToModel(list);
return new PagedCollection<TModel>(resultList, viewCollection.PageIndex, viewCollection.PageSize, viewCollection.TotalItemCount);
}
}

Related

Projection or Custom Value Resolvers with new Multi Lingual Mappings

I am using the newest version of ASP.Net Boilerplate (version 3.7.2). I am currently using the new Multi-Lingual Entities.
I am having issues with the Automapping for 1 of the Entities to a Dto as not only does it require the Multi-Lingual aspect but a Projection/Custom Resolver for one of the properties.
Up to this point all the mappings have been working correctly. I have followed the documentation found at ASP.NET Boilerplate Documentation
[Table("CategoryItems")]
public class CategoryItem : BaseClass, IMultiLingualEntity<CategoryItemTranslation>
{
/// <summary>
/// Gets or Sets the Category Item Display Order
/// </summary>
public int DisplayOrder { get; set; }
/// <summary>
/// Gets or Sets Catalog Categories
/// </summary>
public virtual ICollection<CatalogItem> CatalogItems { get; set; } = new HashSet<CatalogItem>();
...
/// <summary>
/// Gets or Sets all the Entity Translations
/// </summary>
public virtual ICollection<CategoryItemTranslation> Translations { get; set; } = new HashSet<CategoryItemTranslation>();
}
[Table("CategoryItemTranslations")]
public class CategoryItemTranslation : IEntityTranslation<CategoryItem>
{
/// <summary>
/// Get or Set Category Item Name
/// </summary>
public string CategoryItemName { get; set; }
/// <summary>
/// Gets or Sets Link to Category Item
/// </summary>
public CategoryItem Core { get; set; }
/// <summary>
/// Gets or Sets Link to Core Id
/// </summary>
public int CoreId { get; set; }
/// <summary>
/// Gets or Sets the Language
/// </summary>
public string Language { get; set; }
}
public class ReadCategoryItemFilterDto : PhantomEntityDto
{
public string CategoryItemName { get; set; }
...
public int ItemCount { get; set; }
...
}
Using the Create MultiLingual Map code within the Initialize of the ApplicationModule class:
cfg.CreateMultiLingualMap<CategoryItem, CategoryItemTranslation, ReadCategoryItemFilterDto>(new MultiLingualMapContext(IocManager.Resolve<ISettingManager>()));
The CategoryItemName from the Translation Entity correctly maps to the CategoryItemName on the ReadCategoryItemFilterDto.
Now I need to map the CatalogItems Count to the ItemCount on the Dto.
I have tried adding to the Mapping Configurator:
cfg.CreateMap<CategoryItem, ReadCategoryItemFilterDto>()
.ForMember(dest => dest.ItemCount, opts => opts.MapFrom(src => src.CatalogItems.Count));
and
cfg.CreateMap<CategoryItem, ReadCategoryItemFilterDto>()
.ForMember(dest => dest.ItemCount, opts => opts.ResolveUsing(new CatalogItemFilterCountResolver()));
using
public class CatalogItemFilterCountResolver : IValueResolver<CategoryItem, ReadCategoryItemFilterDto, int>
{
public int Resolve(CategoryItem source, ReadCategoryItemFilterDto destination, int m, ResolutionContext context)
{
return source.CatalogItems.Count;
}
}
If I add this map before the CreateMultiLingualMap the CatalogItemName Maps correctly but the ItemCount fails.
If I put the Projection or Resolver after the CreateMultiLingualMap then the ItemCount maps correctly but the CatalogItemName fails to map.
How do I create a Map Profile to allow for both of the properties to be mapped?
Thank you.
For your case, you don't need to use the extension CreateMultiLingualMap.
You can create your own mapping definition with multilingual mapping included.
That's how it is done;
configuration.CreateMap<CategoryItem, ReadCategoryItemFilterDto>()
.BeforeMap((source, destination, context) =>
{
var translation = source.Translations.FirstOrDefault(pt => pt.Language == CultureInfo.CurrentUICulture.Name);
if (translation != null)
{
context.Mapper.Map(translation, destination);
return;
}
var multiLingualMapContext = new MultiLingualMapContext(IocManager.Instance.Resolve<ISettingManager>());
var defaultLanguage = multiLingualMapContext.SettingManager.GetSettingValue(LocalizationSettingNames.DefaultLanguage);
translation = source.Translations.FirstOrDefault(pt => pt.Language == defaultLanguage);
if (translation != null)
{
context.Mapper.Map(translation, destination);
return;
}
translation = source.Translations.FirstOrDefault();
if (translation != null)
{
context.Mapper.Map(translation, destination);
}
}).ForMember(dest => dest.ItemCount, opts => opts.MapFrom(src => src.CatalogItems.Count));
https://aspnetboilerplate.com/Pages/Documents/Multi-Lingual-Entities#createmultilingualmap
configuration.CreateMultiLingualMap<CategoryItem, CategoryItemTranslation, ReadCategoryItemFilterDto>(context)
.EntityMap.ForMember(dest => dest.ItemCount, opt => opt.MapFrom(src => src.CatalogItems.Count));

Datatables pagination does not work

Hello i have following code
jQuery datatables plugin (VIEW)
<script type="text/javascript">
$(document).ready(function () {
$('#table1').DataTable({
/* PROCESS NOTIFICATION */
"processing": true,
/* SERVER SIDE PROCESSING */
"serverSide": true,
"ajax":
{
"url": "GetNonstandardProgram",
"type": "POST"
},
"columnDefs":
[
/* HIDE ID COLUMN */
{
"targets": [ 0 ],
"visible": false,
"searchable": false
},
/* ACTION COLUMN */
{
"aTargets": [-1],
"mData": null,
"mRender": function (data, type, full) {
return 'test';
}
}
],
"bSort":false,
"bPaginate":true,
"iDisplayStart": 5,
"iDisplayLength": 2
});
});
class for parameters which i downloaded (MODEL)
/// <summary>
/// Class that encapsulates most common parameters sent by DataTables plugin
/// </summary>
public class jQueryDataTableParamModel
{
/// <summary>
/// Request sequence number sent by DataTable,
/// same value must be returned in response
/// </summary>
public string sEcho { get; set; }
/// <summary>
/// Text used for filtering
/// </summary>
public string sSearch { get; set; }
/// <summary>
/// Number of records that should be shown in table
/// </summary>
public int iDisplayLength { get; set; }
/// <summary>
/// First record that should be shown(used for paging)
/// </summary>
public int iDisplayStart { get; set; }
/// <summary>
/// Number of columns in table
/// </summary>
public int iColumns { get; set; }
/// <summary>
/// Number of columns that are used in sorting
/// </summary>
public int iSortingCols { get; set; }
/// <summary>
/// Comma separated list of column names
/// </summary>
public string sColumns { get; set; }
}
method which is fired by jQuery (CONTROLLER)
public JsonResult GetNonstandardProgram(SWIN.Models.jQueryDataTableParamModel param)
{
but in this step is a problem.
all values are zeroes or null.
So the question is, why i dont see any parameter values ?!
The datatables is sending this parameters via POST
...
&start=5
&length=2
&search[value]=
...
&search[regex]=false
Thank you
i figured it out.
I changed name of the parameters in the class
public class jQueryDataTableParamModel
{
to this
/// <summary>
/// Number of records that should be shown in table
/// </summary>
public int length { get; set; }
//public int iDisplayLength { get; set; }
/// <summary>
/// First record that should be shown(used for paging)
/// </summary>
public int start { get; set; }
//public int iDisplayStart { get; set; }
and just used param.start and param.length

What should be the best practice in Domain and DTO mapping

I have task to make AbsenceDTO with two properties DateFrom and DateTO where the Domain Absence can be a single day with three properties Day, Month and Year. Bellow is the code sample:
public class Absence
{
#region Properties
/// <summary>
/// A unique id
/// </summary>
public int Id { get; set; }
/// <summary>
/// Day of the absence
/// </summary>
public int Day { get; set; }
/// <summary>
/// Month of the absence
/// </summary>
public int Month { get; set; }
/// <summary>
/// Year of the absence
/// </summary>
public int Year { get; set; }
}
public class AbsenceDTO
{
private List<Absence> absences = null;
public AbsenceDTO()
{
}
public AbsenceDTO(List<Absence> absences)
{
this.absences = absences;
}
public DateTime DateFrom
{
get
{
return //TO DO
}
}
public DateTime DateTO
{
get
{
return //TO DO
}
}
}
So here how should I calculate DTO Date properties and what should be the best pattern/practice in making mapper/converter?

RavenDB - Computed Properties

I have a document with a few computed properties. Properties that have no setter and can call other methods on my class to return a result, example:
public class Order
{
public string CustomerId { get; set; }
public string VehicleId { get; set; }
public DateTime OrderDate { get; set; }
public decimal CommissionPercent { get; set; }
public List<OrdersLines> OrderLines { get; set; }
public decimal Total
{
get { return GetOrderLinesTotal() + SomeDecimal + AnotherDecimal; }
}
public decimal GetOrderLinesTotal()
{
return OrderLines.Sum(x => x.Amount);
}
}
I use a simple index to search for Orders by customer, date and some properties on the Vehicle document using lucene query and a Transformer to create my view models. I've looked at Scripted Index Results and I'm not sure if it would apply in this case.
public class ViewModel
{
public string OrderId { get; set; }
public string CustomerName { get; set; }
public string VehicleName { get; set; }
public string Total { get; set; }
}
How do i get the computed value from the Total property when I query these documents?
I've simplified the GetOrderLinesTotal some what, in fact it is a complex method that takes a lot of other properties into account when calculating the total.
I only get the computed value that serialized when the document was created or updated.
I realized that this is more a of a document design problem rather than trying to do something RavenDB was not meant to do. I simplified my document and used a map/reduce to solve the problem.
I think your situation is similar to a problem I once encountered. I use a JsonIgnore attribute on my public get property and use a private backing field, which I include in serialisation using the JsonProperty attribute. You should be able to apply a similar idea hopefully:
/// <summary>
/// The <see cref="Team" /> class.
/// </summary>
public class Team
{
/// <summary>
/// The ids of the users in the team.
/// </summary>
[JsonProperty(PropertyName = "UserIds")]
private ICollection<string> userIds;
// ...[snip]...
/// <summary>
/// Gets the ids of the users in the team.
/// </summary>
/// <value>
/// The ids of the users in the team.
/// </value>
[JsonIgnore]
public IEnumerable<string> UserIds
{
get { return this.userIds; }
}
// ...[snip]...
}

Nhibernate invalid cast

I am trying to create a DTO object for a HQL query I am running, however, when executing my HQL query from my repository it is producing a NH cast error:
System.InvalidCastException: Unable to cast object of type 'NHibernate.Hql.Ast.ANTLR.Tree.SqlNode' to type 'NHibernate.Hql.Ast.ANTLR.Tree.FromReferenceNode'.
Has anyone come across this before?
My DTO mapping and class are below:
/// <summary>
/// TODO: Update summary.
/// </summary>
public class TaskListItemMapping : ClassMap<TaskListItem>
{
public TaskListItemMapping()
{
ImportType<TaskListItem>();
Id(x => x.TaskCode).GeneratedBy.Assigned();
}
}
/// <summary>
/// A class representing a task list data query result
/// </summary>
public class TaskListItem
{
public virtual int Code { get; set; }
public virtual String Client { get; set; }
public virtual string Matter { get; set; }
public virtual DateTime DueDate { get; set; }
public virtual bool Notepad { get; set; }
public virtual bool Flag { get; set; }
public virtual string Client { get; set; }
public virtual string Issue { get; set; }
public virtual string Grade { get; set; }
public virtual String TaskInitials { get; set; }
public virtual string Description { get; set; }
public TaskListItem()
{
}
}
Any thoughts appreciated!
I found out what was causing this - it was a mis-match on data in the constructor