RavenDB - Computed Properties - ravendb

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]...
}

Related

How to Bind controller to multidimensional query string parameter in .NET Core/5

I have a web client which uses jQuery data tables for server side pagination and ordering. It sends parameters in a specific way through a query string:
Here is an exmpaple of the order parameter that is sent to the server:
&order[0][column]=1&order[0][dir]=asc&start=0&length=10&search[value]=&search[regex]=false&_=1618229879540
I'm having a really hard time trying to bind to these parameters in an ApiController class.
Controller code:
[HttpGet]
public async Task<ActionResult<DataTable<Audit>>> GetAudits([FromQuery]DataTableParams dataTableParams)
{
var audits = await _auditRepository.GetAuditsAsync(dataTableParams);
return Ok(audits);
}
Parameter Classes:
public class DataTableParams
{
public int Draw { get; set; }
public int Start { get; set; }
public int Length { get; set; } = 30;
public string Dir { get; set; }
public IEnumerable<DtOrder> Order { get; set;}
}
public class DtOrder
{
/// <summary>
/// Column to which ordering should be applied.
/// This is an index reference to the columns array of information that is also submitted to the server.
/// </summary>
public int Column { get; set; }
/// <summary>
/// Ordering direction for this column.
/// It will be dt-string asc or dt-string desc to indicate ascending ordering or descending ordering, respectively.
/// </summary>
public string Dir { get; set; }
}
What am I doing wrong? I've tried DtOrder[] instead of IEnumerable. In each case the framework fails to bind to the order parameter, lenght/count is 0.
For those searching for this exact thing.
Datatables sends the data in a specific formatted query string.
There does not appear to be an easy way to bind the data a custom binder needs to be created for this.
There is a NuGet package that specifically deals with helping format and response to datatables requests. DataTables.AspNet.AspNetCore is the package and it has a binder and everything needed to be able to bind to database query strings as well as format the response for datatables.

How can I provide example data for my request / response in Swagger UI?

I'm developing one web service with WebAPI 2 and OWIN. My goal is add some documentation with Swashbuckle.
Following my method:
/// <summary>
/// La mia descrizione...
/// </summary>
/// <param name="id">Identificativo</param>
/// <param name="list">Una lista</param>
[HttpPut]
[Route("~/api/v1/documents/{id}/attribute")]
public IHttpActionResult Put(int id, List<AttributeDto> list)
{
_myService.Create(list, id);
return Ok();
}
Below my AttributeDto class code:
using Newtonsoft.Json;
namespace WebApi.Dtos
{
public class AttributeDto
{
[JsonIgnore]
public int Id { get; set; }
[JsonIgnore]
public int OtherId { get; set; }
public string Label { get; set; }
public string Value { get; set; }
}
}
If I open Swagger UI I can view one example section with autogenerated data:
How can I customize autogenerated data so that JSON in picture becomes like below:
[
{
"label": "Etichetta di esempio",
"value": "Valore di esempio"
}
]
I will recommend you using Swagger-Net instead of swashbuckle.
All you will need to do is decorate the definition property with an xml example comment like this:
public class AttributeDto
{
[JsonIgnore]
public int Id { get; set; }
[JsonIgnore]
public int OtherId { get; set; }
/// <example>Etichetta di esempio</example>
public string Label { get; set; }
/// <example>Valore di esempio</example>
public string Value { get; set; }
}
This was proposed to swashbuckle but is has not been merge there yet:
https://github.com/domaindrivendev/Swashbuckle/pull/1091
Swagger-Net is my fork of Swashbuckle but I've merged many nice features and fixed many bugs.
An option will be to use an ISchemaFilter (that goes on the SwaggerConfig.cs), here is an example:
private class ApplySchemaVendorExtensions : ISchemaFilter
{
public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
if (schema.properties != null)
{
foreach (var p in schema.properties)
{
switch (p.Key)
{
case "label":
p.Value.example = "Etichetta di esempio";
break;
case "value":
p.Value.example = "Valore di esempio";
break;
}
break;
}
}
}
}
I personally don't love that solution because we will have to maintain information for a class in two different files...

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?

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

NHibernate projection, AutoMapping, IPagedList, how to?

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