XML to be deserialized:
<CheckOnlineStatus xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Lifecare.LIS.Integration.Hyland.Models">
<Database>string</Database>
<InstancesStillNearline>0</InstancesStillNearline>
<SeriesLocationMap xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d2p1:KeyValueOfstringboolean>
<d2p1:Key>test</d2p1:Key>
<d2p1:Value>true</d2p1:Value>
</d2p1:KeyValueOfstringboolean>
<d2p1:KeyValueOfstringboolean>
<d2p1:Key>test1</d2p1:Key>
<d2p1:Value>false</d2p1:Value>
</d2p1:KeyValueOfstringboolean>
</SeriesLocationMap>
<Status>string</Status>
<StudyInstanceUID>string</StudyInstanceUID>
<TotalClips>0</TotalClips>
<TotalExecutionTime>0</TotalExecutionTime>
<TotalInstances>0</TotalInstances>
</CheckOnlineStatus>
Data Models used:
using System.Collections.Generic;
using System.Xml.Serialization;
[XmlRoot("CheckOnlineStatus")]
/// <summary>
/// The CheckOnlineStatus entity.
/// </summary>
public class CheckOnlineStatus
{
[XmlElement("Status")]
/// <summary>
/// Gets or sets the Status
/// </summary>
public string Status { get; set; }
[XmlElement("Database")]
/// <summary>
/// Gets or sets the Database
/// </summary>
public string Database { get; set; }
/// <summary>
/// Gets or sets the StudyInstanceUID
/// </summary>
[XmlElement("StudyInstanceUID")]
public string StudyInstanceUID { get; set; }
/// <summary>
/// Gets or sets the TotalExecutionTime
/// </summary>
[XmlElement("TotalExecutionTime")]
public long TotalExecutionTime { get; set; }
/// <summary>
/// Gets or sets the TotalInstances
/// </summary>
[XmlElement("TotalInstances")]
public int TotalInstances { get; set; }
/// <summary>
/// Gets or sets the TotalClips
/// </summary>
[XmlElement("TotalClips")]
public int TotalClips { get; set; }
/// <summary>
/// Gets or sets the InstancesStillNearline
/// </summary>
[XmlElement("InstancesStillNearline")]
public int InstancesStillNearline { get; set; }
/// <summary>
/// Gets or sets the dictionary of series location map
/// </summary>
[XmlElement("SeriesLocationMap")]
public Dictionary<string, bool> SeriesLocationMap { get; set; }
}
Deserialization code, here result is above xml in string format:
(CheckOnlineStatus)new XmlSerializer(typeof(CheckOnlineStatus)).Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(result)));
I am getting below runtime error:
Cannot serialize member
Lifecare.LIS.Integration.NDP.Models.CheckOnlineStatus.SeriesLocationMap
of type System.Collections.Generic.Dictionary`2[[System.String,
mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089],[System.Boolean, mscorlib,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]],
because it implements IDictionary.
I tried couple of other things but it did not worked out e.g. https://www.codeproject.com/Questions/716556/XML-deserialize-Attributes-to-dictionary
Can someone please help here. Thanks.
As the exception (InvalidOperationException with an inner UnsupportedException) says you can't simply deserialize it into a Dictionary.
Here is one way to overcome of this difficulty:
Define KeyValue
[Serializable]
public struct KeyValue
{
public string Key { get; set; }
public bool Value { get; set; }
}
As an alternative you might take advantage of the built-in KeyValuePair<TKey, TValue>
Use XmlArray and XmlArrayItem
[XmlArray("SeriesLocationMap")]
[XmlArrayItem("KeyValueOfstringboolean")]
public List<KeyValue> IntermediateContainer { get; set; }
Use XmlIgnore to convert List to Dictionary<string, bool>
[XmlIgnore]
public Dictionary<string, bool> SeriesLocationMap => IntermediateContainer.ToDictionary(x => x.Key, y => y.Value);
I've tested it without namespaces, so you need to specify them at the right place to be able to read them properly.
Alternatively you might give it a try and use DataContractSerializer which does support Dictionary.
Related
I trust you doing good, I am new to .net core API, I am doing a model validation, seems like it does not work,
Controller Action :
[HttpPost]
[ValidateModel]
[ProducesResponseType(StatusCodes.Status201Created, Type = typeof(ActionLog))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ActionLog))]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<IActionResult> CreateAsync([FromBody] ActionLog document)
{
try
{
if (ModelState.IsValid)
{
var result = await this.documentRepository.CreateAsync(document);
return this.StatusCode(StatusCodes.Status201Created,document.Id);
}
else
{
return this.StatusCode(StatusCodes.Status400BadRequest);
}
}
catch (Exception)
{
return this.StatusCode(StatusCodes.Status500InternalServerError, "Internal Server Error");
}
}
Model Class
public class ActionLog : ILogDocument
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>
/// The identifier.
/// </value>
[BsonElement("id")]
[BsonId(IdGenerator = typeof(ObjectIdGenerator))]
public ObjectId Id { get; set; }
/// <summary>
/// Gets or sets the source identifier.
/// </summary>
/// <value>
/// The source identifier. Used to identify which source the log came from.
/// </value>
[BsonRequired]
[BsonElement("sourceId")]
public string SourceId { get; set; }
/// <summary>
/// Gets or sets the keywords.
/// </summary>
/// <value>
/// The keywords associated to the log.
/// Those keywords allow for faster searches compared to a full-text search on the payload.
/// This should be used as much as possible for common actions like: subject, type of action or target resource.
/// </value>
[BsonElement("keywords")]
public string[] Keywords { get; set; } = Array.Empty<string>();
/// <summary>
/// Gets or sets the payload of the action log (usually more detailed data).
/// </summary>
/// <value>
/// The payload.
/// </value>
[BsonElement("payload")]
public string Payload { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the date of event occurrence.
/// </summary>
/// <value>
/// The date.
/// </value>
[BsonDateTimeOptions(Kind = DateTimeKind.Utc)]
[BsonElement("date")]
public DateTime Date { get; set; } = DateTime.UtcNow;
}
Apart from that I have not coded any configurations in other classes or configuration, It returns 201 for every request.
reference : https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web-api
I am trying to find the most efficient way to display a set of nested comments from a database schema and into a c# object that i can convert to a serialised JSON object.
My Schema is as follows:
As you can see from the screenshot there is an Update which has a parent issue id (not relevant here) and a Comment. There is also a ParentUpdateID which enables the table to store nested replies for each reply and so on.
I have created a class within c# which i want to convert the results from my Entity Framework call;
/// <summary>
/// The resource view model.
/// </summary>
public class Comment
{
/// <summary>
/// Gets or sets the id.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
public string Body { get; set; }
/// <summary>
/// Gets or sets the modified by.
/// </summary>
public string ModifiedBy { get; set; }
/// <summary>
/// Gets or sets the modified date.
/// </summary>
public DateTime ModifiedDate { get; set; }
/// <summary>
/// Gets or sets the created by.
/// </summary>
public string CreatedBy { get; set; }
/// <summary>
/// Gets or sets the created date.
/// </summary>
public DateTime CreatedDate { get; set; }
/// <summary>
/// Gets or sets the parent comment id.
/// </summary>
public Guid? ParentCommentId { get; set; }
/// <summary>
/// Gets or sets the children.
/// </summary>
public List<Comment> Children { get; set; }
}
And here is how i am retrieving the List of Comments / Updates from the database.
List<ClientIssuesUpdate> updates = this.db.ClientIssuesUpdates.Where(i => i.IssueId == issueId).ToList();
Is there a clever way through either a loop or using lync that can order all of the comments showing most recent first, but then display in date order each comments children and so on. (e.g. All comments without a ParentUpdateId would be to level comments)
Any guidance would be helpful so i can learn for the future.
Many thanks
This should order them by date created.
Depending on how deep your comments children gets nested, you would have to add more loops.
List<ClientIssuesUpdate> updates = this.db.ClientIssuesUpdates.Where(i => i.IssueId == issueId).ToList().OrderByDescending(p => p.CreatedDate);
foreach (var comment in updates.Where(comment => comment.Children != null))
{
// order the children
comment.Children = new List<Comment>(comment.Children.OrderByDescending(c => c.CreatedDate));
}
*cant remember if you should use OrderBy<> or OrderByDescending<>
I would like to create a generic notification engine. The idea is to have a single core engine to process any type of notification. This engine will process notification and handle all logging, error handling etc..
I created 3 simple interfaces:
public interface INotificationInput
{
/// <summary>
/// Friendly Name for logging/tracing usage
/// </summary>
string FriendlyName { get; set; }
string NotificationCode{ get; set; }
Double Version { get; set; }
}
public interface INotificationOutput
{
/// <summary>
/// Friendly Name for logging/tracing usage
/// </summary>
string FriendlyName { get; }
}
public interface INotificationProvider<out Toutput, Tinput> where Toutput : INotificationOutput where Tinput : INotificationInput
{
/// <summary>
/// Friendly Name for logging/tracing usage
/// </summary>
string FriendlyName { get; set; }
/// <summary>
/// Generates and returns an INotificationOutput from data
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
Toutput GenerateNotificationOutput(Tinput data);
}
So the INotificationProvider will chunk the INotificationInput to create a INotificationOutput.
That could be information to send a email, a sms, you name it, the engine will call the methods and do the magic of scheduling, logging, handling errors and so on..
I implemented the interface like this:
/// <summary>
/// INotificationInput represented by a dummy object
/// </summary>
public class DummyNotificationInput : INotificationInput
{
public string FriendlyName { get; set; }
public string NotificationCode { get; set; }
public double Version { get; set; }
}
public class DummyNotificationOutput : INotificationOutput
{
public string FriendlyName { get; private set; }
}
public class DummyProvider : INotificationProvider<DummyNotificationOutput, DummyNotificationInput>
{
public string FriendlyName { get; set; }
public DummyNotificationOutput GenerateNotificationOutput(DummyNotificationInput data)
{
throw new NotImplementedException();
}
}
Now I would like my engine to have a list of provider:
var providersList = new List<INotificationProvider<INotificationOutput, INotificationInput>>();
The problem is that I cannot to the following:
providersList.Add(new DummyProvider<DummyNotificationOutput, DummyNotificationInput>());
There must be a solution. Am I using the wrong approach?
The second generic type argument to INotificationProvider isn't covariant (at a conceptual level), but you're trying to use it as if it were. It is actually contravariant.
In your list of INotificationProvider objects you've defined the input notification as an INotificationInput. This means objects added to this list need to be able to accept any type of INotificationInput as input to their GenerateNotificationOutput function. You're trying to add an object that only knows how to handle DummyNotificationInput objects. It would fail if it were passed some other type of input.
Either your provider needs to accept INotificationInput objects, if you want to be able to add it to that list, or the list needs to define all of the objects as accepting DummyNotificationInput.
As Servy has already answered, you can't really do this due to what you providersList is expecting
With this in mind, it may actually be simpler to just make INotificationProvider non-generic:
public interface INotificationProvider
{
/// <summary>
/// Friendly Name for logging/tracing usage
/// </summary>
string FriendlyName { get; set; }
/// <summary>
/// Generates and returns an INotificationOutput from data
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
INotificationOutput GenerateNotificationOutput(INotificationInput data);
}
Then the DummyProvider becomes:
public class DummyProvider : INotificationProvider
{
public string FriendlyName { get; set; }
public INotificationOutput GenerateNotificationOutput(INotificationInput data)
{
throw new NotImplementedException();
}
}
Now, probably not what you had in mind - you are expecting to pass DummyNotificationInput instances to DummyProvider
You could just type check in your Provider code
public class DummyProvider : INotificationProvider
{
public string FriendlyName { get; set; }
public INotificationOutput GenerateNotificationOutput(INotificationInput data)
{
if (!(data is DummyNotificationInput)) throw new ArgumentException("Invalid type specified", "data");
return something...;
}
}
Obviously, you lose design time checking - but if you really need to put them in a covariant list you can't provide an implementor that has a derived generic type argument
In Datatables 1.10 the ajax server side parameters changed from
public class DataTableParamModel
{
public string sEcho{ get; set; }
public string sSearch{ get; set; }
public int iDisplayLength{ get; set; }
public int iDisplayStart{ get; set; }
public int iColumns{ get; set; }
public int iSortingCols{ get; set; }
public string sColumns{ get; set; }
}
to (API Here http://datatables.net/manual/server-side)
columns[i][data]
columns[i][name]
columns[i][orderable]
columns[i][search][regex]
columns[i][search][value]
columns[i][searchable]
...
draw
length
order[i][column]
order[i][dir]
...
search[regex]
search[value]
start
Some are easy to bind
public class DataTableParamModel
{
public string draw { get; set; }
public int length{ get; set; }
public int start { get; set; }
}
But the new array format looks tricky.
What is the new appropriate model to map the new parameter format?
Here is a model binder and class that will bind these new parameters...
Nuget Package: https://www.nuget.org/packages/Wetware.DataTables
Parameter Model:
[ModelBinder(typeof(DTModelBinder))]
public class DTParameterModel
{
/// <summary>
/// Draw counter. This is used by DataTables to ensure that the Ajax returns from
/// server-side processing requests are drawn in sequence by DataTables
/// </summary>
public int Draw { get; set; }
/// <summary>
/// Paging first record indicator. This is the start point in the current data set
/// (0 index based - i.e. 0 is the first record)
/// </summary>
public int Start { get; set; }
/// <summary>
/// Number of records that the table can display in the current draw. It is expected
/// that the number of records returned will be equal to this number, unless the
/// server has fewer records to return. Note that this can be -1 to indicate that
/// all records should be returned (although that negates any benefits of
/// server-side processing!)
/// </summary>
public int Length { get; set; }
/// <summary>
/// Global Search for the table
/// </summary>
public DTSearch Search { get; set; }
/// <summary>
/// Collection of all column indexes and their sort directions
/// </summary>
public IEnumerable<DTOrder> Order { get; set; }
/// <summary>
/// Collection of all columns in the table
/// </summary>
public IEnumerable<DTColumn> Columns { get; set; }
}
/// <summary>
/// Represents search values entered into the table
/// </summary>
public sealed class DTSearch
{
/// <summary>
/// Global search value. To be applied to all columns which have searchable as true
/// </summary>
public string Value { get; set; }
/// <summary>
/// true if the global filter should be treated as a regular expression for advanced
/// searching, false otherwise. Note that normally server-side processing scripts
/// will not perform regular expression searching for performance reasons on large
/// data sets, but it is technically possible and at the discretion of your script
/// </summary>
public bool Regex { get; set; }
}
/// <summary>
/// Represents a column and it's order direction
/// </summary>
public sealed 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 asc or desc to indicate ascending
/// ordering or descending ordering, respectively
/// </summary>
public string Dir { get; set; }
}
/// <summary>
/// Represents an individual column in the table
/// </summary>
public sealed class DTColumn
{
/// <summary>
/// Column's data source
/// </summary>
public string Data { get; set; }
/// <summary>
/// Column's name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Flag to indicate if this column is orderable (true) or not (false)
/// </summary>
public bool Orderable { get; set; }
/// <summary>
/// Flag to indicate if this column is searchable (true) or not (false)
/// </summary>
public bool Searchable { get; set; }
/// <summary>
/// Search to apply to this specific column.
/// </summary>
public DTSearch Search { get; set; }
}
Model Binder:
/// <summary>
/// Model Binder for DTParameterModel (DataTables)
/// </summary>
public class DTModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
base.BindModel(controllerContext, bindingContext);
var request = controllerContext.HttpContext.Request;
// Retrieve request data
var draw = Convert.ToInt32(request["draw"]);
var start = Convert.ToInt32(request["start"]);
var length = Convert.ToInt32(request["length"]);
// Search
var search = new DTSearch
{
Value = request["search[value]"],
Regex = Convert.ToBoolean(request["search[regex]"])
};
// Order
var o = 0;
var order = new List<DTOrder>();
while (request["order[" + o + "][column]"] != null)
{
order.Add(new DTOrder
{
Column = Convert.ToInt32(request["order[" + o + "][column]"]),
Dir = request["order[" + o + "][dir]"]
});
o++;
}
// Columns
var c = 0;
var columns = new List<DTColumn>();
while (request["columns[" + c + "][name]"] != null)
{
columns.Add(new DTColumn
{
Data = request["columns[" + c + "][data]"],
Name = request["columns[" + c + "][name]"],
Orderable = Convert.ToBoolean(request["columns[" + c + "][orderable]"]),
Searchable = Convert.ToBoolean(request["columns[" + c + "][searchable]"]),
Search = new DTSearch
{
Value = request["columns[" + c + "][search][value]"],
Regex = Convert.ToBoolean(request["columns[" + c + "][search][regex]"])
}
});
c++;
}
return new DTParameterModel
{
Draw = draw,
Start = start,
Length = length,
Search = search,
Order = order,
Columns = columns
};
}
}
Usage:
MyController.cs
public JsonResult DataTablesList(DTParameterModel model)
{
...
}
MVC6
If you're going to MVC6 you no longer need the model binder as MVC6 includes a JQueryFormValueProvider into the default model binder that can bind these values.
The model classes themselves may still be useful, however.
There is a bug to be fixed in 2.1.0 that doesn't allow binding for HttpGet but still works for HttpPost
Give this a try #shoe: datatables-mvc project: https://github.com/ALMMa/datatables-mvc
I changed my javascript to use the legacy ajax params option which uses the old parameters to send to the server. This is done through $.fn.dataTable.ext.legacy.ajax = true; so now my code becomes something like...
$.fn.dataTable.ext.legacy.ajax = true;
var datatable = $('#data-table').DataTable({
"processing": true,
"serverSide": true,
"ajax": "MyController/AjaxHandlerPaging",
"pageLength": 25,
"order": [[2, 'desc']],
"columns": []
});
Know this post is 2 years old but to those who want to use this with ASP.Net Core MVC 6. This is the converted/ upgraded answer provided by #Shoe
Model Binder:
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TrackingAndTraining.Models
{
/// <summary>
/// Model Binder for DTParameterModel (DataTables)
/// </summary>
public class DTModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var request = bindingContext.ActionContext.HttpContext.Request.Form;
// Retrieve request data
var draw = Convert.ToInt32(request["draw"]);
var start = Convert.ToInt32(request["start"]);
var length = Convert.ToInt32(request["length"]);
// Search
var search = new DTSearch
{
Value = request["search[value]"],
Regex = Convert.ToBoolean(request["search[regex]"])
};
// Order
var o = 0;
var order = new List<DTOrder>();
while (!string.IsNullOrEmpty(request["order[" + o + "][column]"]))
{
order.Add(new DTOrder
{
Column = Convert.ToInt32(request["order[" + o + "][column]"]),
Dir = request["order[" + o + "][dir]"]
});
o++;
}
// Columns
var c = 0;
var columns = new List<DTColumn>();
while (!string.IsNullOrEmpty(request["columns[" + c + "][name]"]))
{
columns.Add(new DTColumn
{
Data = request["columns[" + c + "][data]"],
Name = request["columns[" + c + "][name]"],
Orderable = Convert.ToBoolean(request["columns[" + c + "][orderable]"]),
Searchable = Convert.ToBoolean(request["columns[" + c + "][searchable]"]),
Search = new DTSearch
{
Value = request["columns[" + c + "][search][value]"],
Regex = Convert.ToBoolean(request["columns[" + c + "][search][regex]"])
}
});
c++;
}
var result = new DTParameterModel
{
Draw = draw,
Start = start,
Length = length,
Search = search,
Order = order,
Columns = columns
};
bindingContext.Result = ModelBindingResult.Success(result);
return TaskCache.CompletedTask;
}
}
}
Parameter Model:
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TrackingAndTraining.Models
{
[ModelBinder(BinderType = typeof(DTModelBinder))]
public class DTParameterModel
{
/// <summary>
/// Draw counter. This is used by DataTables to ensure that the Ajax returns from
/// server-side processing requests are drawn in sequence by DataTables
/// </summary>
public int Draw { get; set; }
/// <summary>
/// Paging first record indicator. This is the start point in the current data set
/// (0 index based - i.e. 0 is the first record)
/// </summary>
public int Start { get; set; }
/// <summary>
/// Number of records that the table can display in the current draw. It is expected
/// that the number of records returned will be equal to this number, unless the
/// server has fewer records to return. Note that this can be -1 to indicate that
/// all records should be returned (although that negates any benefits of
/// server-side processing!)
/// </summary>
public int Length { get; set; }
/// <summary>
/// Global Search for the table
/// </summary>
public DTSearch Search { get; set; }
/// <summary>
/// Collection of all column indexes and their sort directions
/// </summary>
public List<DTOrder> Order { get; set; }
/// <summary>
/// Collection of all columns in the table
/// </summary>
public List<DTColumn> Columns { get; set; }
}
/// <summary>
/// Represents search values entered into the table
/// </summary>
public sealed class DTSearch
{
/// <summary>
/// Global search value. To be applied to all columns which have searchable as true
/// </summary>
public string Value { get; set; }
/// <summary>
/// true if the global filter should be treated as a regular expression for advanced
/// searching, false otherwise. Note that normally server-side processing scripts
/// will not perform regular expression searching for performance reasons on large
/// data sets, but it is technically possible and at the discretion of your script
/// </summary>
public bool Regex { get; set; }
}
/// <summary>
/// Represents a column and it's order direction
/// </summary>
public sealed 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 asc or desc to indicate ascending
/// ordering or descending ordering, respectively
/// </summary>
public string Dir { get; set; }
}
/// <summary>
/// Represents an individual column in the table
/// </summary>
public sealed class DTColumn
{
/// <summary>
/// Column's data source
/// </summary>
public string Data { get; set; }
/// <summary>
/// Column's name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Flag to indicate if this column is orderable (true) or not (false)
/// </summary>
public bool Orderable { get; set; }
/// <summary>
/// Flag to indicate if this column is searchable (true) or not (false)
/// </summary>
public bool Searchable { get; set; }
/// <summary>
/// Search to apply to this specific column.
/// </summary>
public DTSearch Search { get; set; }
}
}
Again all credit goes to #Shoe for original post.
I ran into the same issue when moving to 1.10. Basically, I changed my parameter class like this (getting rid of the unsupported parameters):
public class jQueryDataTableParamModel
{
/// <summary>
/// Request sequence number sent by DataTable,
/// same value must be returned in response
/// </summary>
public string draw { get; set; }
/// <summary>
/// Number of records that should be shown in table
/// </summary>
public int length { get; set; }
/// <summary>
/// First record that should be shown(used for paging)
/// </summary>
public int start { get; set; }
}
In my controller, I get the search value, sort order, and sort column like this:
var searchString = Request["search[value]"];
var sortColumnIndex = Convert.ToInt32(Request["order[0][column]"]);
var sortDirection = Request["order[0][dir]"]; // asc or desc
The full server side binding implementation can be found here. I ran into a similar issue and this was how I went about solving it.
I've run into a issue when using JsonConvert.SerializeObject(addToBasketView) as it seems not to like tha list that i pass into it - until i passed in the list it was working fine - are you not able to pass a list?
Here is my code:
controller:
//We need a list of Automation scripts - this was in Apollo but now we need to pass it in teh message
var automationList = AutomationHelper(retailerId, productId);
var addToBasketView = new AddToBasketView
{
Url = retailerProduct.DeepLink,
Password = (password),
Username = (username),
RetailerProductJson = new RetailerProductJson(retailerProduct),
WidgetImpressionId = id,
Quantity = qty,
MessageId = messageId,
AutomationList = automationList
};
// now turn it into a string
var json = JsonConvert.SerializeObject(addToBasketView);
Addtobasketview.cs:
using System;
using System.Collections.Generic;
namespace WidgetData
{
/// <summary>
/// This is the view that we will give to the AddToBasketForm
/// </summary>
[Serializable]
public class AddToBasketView
{
/// <summary>
/// The Username for the retailer
/// </summary>
public String Username { get; set; }
/// <summary>
/// The password for the retailer
/// </summary>
public String Password { get; set; }
/// <summary>
/// The URl of the thing they want added to the site
/// </summary>
public String Url { get; set; }
/// <summary>
/// The retailer product selected - from this I can get the retailer and the product.
/// </summary>
public RetailerProductJson RetailerProductJson { get; set; }
/// <summary>
/// The widget impression id so that we can attach to the addTobaskets table for recording purposes
/// </summary>
public int WidgetImpressionId { get; set; }
/// <summary>
/// set the quantity
/// </summary>
public int Quantity { get; set; }
/// <summary>
/// The MessageId this is so we can identify it when waiting for a response
/// </summary>
public String MessageId { get; set; }
/// <summary>
/// Automation script list
/// </summary>
public List<AutomationStepScript> AutomationList { get; set; }
}
}
Edit:
AutomationStepScript.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace WidgetData
{
/// <summary>
/// Extends the class created by Linq To SQL, so that we can control in our models section the validity of the data
/// </summary>
[MetadataType(typeof(AuotmationStepScriptValidation))]
public partial class AutomationStepScript
{
/// <summary>
/// Override the to string so that we can use in the delete method to capture details of this record before it is deleted
/// </summary>
/// <returns></returns>
public override string ToString()
{
return string.Format("AutomationStepScript Id = {0}, AutomationStepId = {1}, Sort Order = {2}, Description = {3}, Script = {4}, Evaluate = {5}", Id,AutomationStepId,SortOrder,Description,Script,Evaluate);
}
}
/// <summary>
/// Class to validate my AuotmationStepScript
/// </summary>
public class AuotmationStepScriptValidation
{
/// <summary>
/// AutomationId
/// </summary>
[Required(ErrorMessage = "AutomationStepId is a required field")]
[Range(1, int.MaxValue, ErrorMessage = "AutomationStepId must be valid")]
public int AutomationStepId { get; set; }
/// <summary>
/// Display SortOrder
/// </summary>
[Range(0, int.MaxValue, ErrorMessage = "SortOrder must be valid")]
public int SortOrder { get; set; }
/// <summary>
/// Description
/// </summary>
[StringLength(256, ErrorMessage = "Description maximum length is 256", MinimumLength = 1)]
public String Description { get; set; }
/// <summary>
/// Script
/// </summary>
[AllowHtml]
[StringLength(8000, ErrorMessage = "Script maximum length is 8000")]
public String Script { get; set; }
/// <summary>
/// Evaluate
/// </summary>
public int Evaluate { get; set; }
}
}
AutomationHelper (called in teh controller to get the list:
/// <summary>
/// The helper function to fetch the data so that we can get the scripts for each step for this automation (which we will find from the localParams)
/// </summary>
/// <param name="retailerId"> </param>
/// <param name="productId"> </param>
/// <returns>WE return a List of AutomationStepScripts, this object will be selected further with Lambda expression"></returns>
public List<AutomationStepScript> AutomationHelper(int retailerId, int productId)
{
List<AutomationStepScript> automationStepScripts = null;
var automationStepScriptRepository = new AutomationStepScriptRepository();
var productRepository = new ProductRepository();
var retailerCategoryRepository = new RetailerCategoryRepository();
var product = productRepository.GetProduct(productId);
var categoryId = product.CategoryId;
var retailerCategory = retailerCategoryRepository.GetRetailerCategoryByRetailerCategory(retailerId, categoryId);
// DO we have a retailer category?
if (retailerCategory != null)
{
// Yes, without a valid retailer category we cannot possibly find the automation
// Now here we have the RetailerCategory. The next question is Does this RetailerCategory HAVE an automation set up for it
if (retailerCategory.AutomationId != null)
{
// Yes, we have an automation. So lets get all of the Scripts for all of the steps for this automation
// Get All scripts for all steps for this automation.
automationStepScripts = automationStepScriptRepository.GetAllAutomationStepScriptsForAutomation((int)retailerCategory.AutomationId).ToList();
}
}
return automationStepScripts;
}
I am serializinf it as it bacomes teh body of a message which i am using in SQS which i then use later.
Please let me know if you need any more information.
Thanks.