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));
Related
This question already has answers here:
Why serializing Version with JsonPropertyAttribute doesn't work?
(1 answer)
parsing an enumeration in JSON.net
(1 answer)
How to tell Json.Net globally to apply the StringEnumConverter to all enums
(5 answers)
Closed 5 years ago.
I have my RestSharpJsonNetSerializer class below.
I am serializing an object that has several properties of enum types.
I am using properties below. But the resulting JSON still displays int values of enums, not their string values.
I guess I could be using wrong properties or need to add changes to my serializer class. Relatively new with this, so need some help please.
public class PTypeData
{
[JsonProperty (ItemConverterType = typeof(StringEnumConverter))]
public Enums.Type Type {get;set;}
[JsonProperty (ItemConverterType = typeof(StringEnumConverter))]
public Enums.Status Status {get;set;}
}
RestSharpJsonNetSerializer class:
public class RestSharpJsonNetSerializer : RestSharp.Serializers.ISerializer
{
private readonly Newtonsoft.Json.JsonSerializer _serializer;
/// <summary>
/// Default serializer
/// </summary>
public RestSharpJsonNetSerializer () {
ContentType = "application/json";
_serializer = new Newtonsoft.Json.JsonSerializer {
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Include
};
}
/// <summary>
/// Default serializer with overload for allowing custom Json.NET settings
/// </summary>
public RestSharpJsonNetSerializer (Newtonsoft.Json.JsonSerializer serializer){
ContentType = "application/json";
_serializer = serializer;
}
/// <summary>
/// Serialize the object as JSON
/// </summary>
/// <param name="obj">Object to serialize</param>
/// <returns>JSON as String</returns>
public string Serialize(object obj) {
using (var stringWriter = new StringWriter()) {
using (var jsonTextWriter = new JsonTextWriter(stringWriter)) {
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
_serializer.Serialize(jsonTextWriter, obj);
var result = stringWriter.ToString();
return result;
}
}
}
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string DateFormat { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string RootElement { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// Content type for serialized content
/// </summary>
public string ContentType { get; set; }
}
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]...
}
I need to get this query using NHibernate:
Select RequestStatus.[Status], Count(ApprovalRequest.Id)
From ApprovalRequest Inner Join RequestStatus On ApprovalRequest.CurrentStatusId = RequestStatus.Id
Where RequestStatus.[Status] In ('Approved', 'Queried') And ApprovalRequest.Deleted != 1
Group By RequestStatus.[Status]
here are my classes:
public class ApprovalRequest
{
public ApprovalRequest()
{
Id = Guid.NewGuid();
Statuses = new List<RequestStatus>();
}
/// <summary>
/// Identity No.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Represents the current status of the request e.g. Approved.
/// </summary>
public RequestStatus CurrentStatus { get; set; }
/// <summary>
/// The statuses that the request experiences.
/// </summary>
public IList<RequestStatus> Statuses { get; set; }
}
}
public class RequestStatus
{
public RequestStatus()
{
Id = Guid.NewGuid();
}
public Guid Id { get; set; }
public StatusEnum Status { get; set; }
}
I tried this query :
var lst = uow.Session.QueryOver<ApprovalRequest>()
.JoinQueryOver<RequestStatus>(req => req.CurrentStatus)
.SelectList(list =>
list
.SelectCount(p => p.Id)
.SelectGroup(p => p.CurrentStatus.Status)
).List();
but I got an error:
could not resolve property: CurrentStatus.Status of: ApprovalRequest (NHibernate)
You need to bind your joined table to an alias.
ApprovalRequest approvalRequestAlias = null;
RequestStatus requestStatusAlias = null;
var lst = uow.Session.QueryOver<ApprovalRequest>(() => approvalRequestAlias)
.JoinAlias(() => approvalRequestAlias.CurrentStatus, () => requestStatusAlias)
.SelectList(list => list
.SelectCount(p => p.Id)
.SelectGroup(p => requestStatusAlias.Status)
).List();
I'm getting 0 results with the following, which I don't think should be the case.
Query code:
var sourceResults = session.Advanced.LuceneQuery<Route>("Routes/BySource")
.WithinRadiusOf(5, toMatch.Source.Location.Latitude, toMatch.Source.Location.Longitude)
.WhereBetween(r => r.DateTime, minDateTime, maxDateTime).ToArray();
Index Code:
/// <summary>
/// Index for spatial search by route source.
/// </summary>
public class Routes_BySource : AbstractIndexCreationTask<Route>
{
public Routes_BySource()
{
Map = routes => from r in routes
select new { _ = SpatialIndex.Generate(r.Source.Location.Latitude, r.Source.Location.Longitude) };
}
}
Model:
public class Route
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>
/// The id.
/// </value>
public string Id { get; set; }
/// <summary>
/// Gets or sets the source.
/// </summary>
/// <value>
/// From.
/// </value>
public Address Source { get; set; }
/// <summary>
/// Gets or sets destination.
/// </summary>
/// <value>
/// To.
/// </value>
public Address Destination { get; set; }
/// <summary>
/// Gets or sets the id of the user who requested the route.
/// </summary>
/// <value>
/// The user id.
/// </value>
public string UserId { get; set; }
/// <summary>
/// Gets or sets the date time that the request is for.
/// </summary>
/// <value>
/// The date time.
/// </value>
public DateTime DateTime { get; set; }
}
public class Address
{
/// <summary>
/// Gets or sets the name. This is the formatted full name of the address.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the location.
/// </summary>
/// <value>
/// The location.
/// </value>
public Location Location { get; set; }
}
public class Location
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
Unit test (calls the query code)
using (var session = Common.DocumentStore.OpenSession())
{
var routeService = new RouteService(session);
var newRoute = new Route
{
Id = Guid.NewGuid().ToString(),
DateTime = DateTime.Now,
UserId = "user",
Source = new Address { Name = "101 W. 79th St. NY, NY", Location = new Location { Latitude = 32, Longitude = 72 } },
Destination = new Address { Name = "101 W. 79th St. NY, NY", Location = new Location { Latitude = 32, Longitude = 72 } }
};
routeService.Save(newRoute);
var routes = routeService.Search(newRoute);
Assert.IsTrue(routes.Any());
}
Any idea what I'm doing wrong here? Seems like it should be pretty straightforward...
Thanks
In your code you have routeService.Save(newRoute);, I'm used to seeing session.Store(newRoute); then session.SaveChanges();
Things don't get written to the database until you call session.SaveChanges();
But you need to give ravenDB time to index the changes. I suggest to use the "wait for non stale results", I don't remember the code for that right now but if you google ravendb non-stale results.
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);
}
}