How to serialize an object using it parents Interfaces? - serialization

Using c# 8. I have a set of base Interfaces with default implementation:
public interface IEventBase
{
string PostRoutingKey { get; set; }
string EventSource => Assembly.GetEntryAssembly()?.GetName().Name;
long Timestamp => DateTimeOffset.UtcNow.ToUnixTimeSeconds();
Guid EventId => Guid.NewGuid();
string EventKey { get; set; }
}
public interface IActionNotifiable : IEventBase
{
[JsonProperty(Required = Required.Always)]
string SenderName { get; set; }
[JsonProperty(Required = Required.Always)]
string ReceiverName { get; set; }
[JsonProperty(Required = Required.Always)]
string SenderId { get; set; }
[JsonProperty(Required = Required.Always)]
string ReceiverId { get; set; }
string Title { get; set; }
string ShortDescription { get; set; }
string LongDescription { get; set; }
ActionNotifiableStatusEnum Status { get; set; }
Dictionary<string, string> ExtraProperties { get; set; }
}
public interface IPush : IActionNotifiable
{
[JsonProperty(Required = Required.Always)]
public string CallbackUrl { get; set; }
}
public class DerivedConcretePush : IPush
{
public string PostRoutingKey { get; set; }= string.Empty;
public string EventKey { get; set; }= string.Empty;
public string SenderName { get; set; }= string.Empty;
public string ReceiverName { get; set; }= string.Empty;
public string SenderId { get; set; }= string.Empty;
public string ReceiverId { get; set; }= string.Empty;
public string Title { get; set; }= string.Empty;
public string ShortDescription { get; set; }= string.Empty;
public string LongDescription { get; set; }= string.Empty;
public ActionNotifiableStatusEnum Status { get; set; }
public Dictionary<string, string> ExtraProperties { get; set; } = new Dictionary<string, string>();
public string CallbackUrl { get; set; }= string.Empty;
}
And trying to Serialize the object using the SerilizeObject from https://www.newtonsoft.com/json, doing something like this:
var message = JsonConvert.SerializeObject(#event, JsonConvertExtension.GetCamelCaseSettings());
and my JsonSettings looks like this:
public static JsonSerializerSettings GetCamelCaseSettings()
{
return new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
},
Formatting = Formatting.Indented,
TypeNameHandling = TypeNameHandling.Auto
};
}
I get something like this:
{
"postRoutingKey": "",
"eventKey": "sericy-rabbiteventconsumer-cli.DerivedConcretePush",
"senderName": "",
"receiverName": "",
"senderId": "",
"receiverId": "",
"title": "just a tittle",
"shortDescription": "",
"longDescription": "body",
"status": 0,
"extraProperties": {},
"callbackUrl": ""
}
I had tried playing around with TypeNameHandling and passing the IEventBase type to SerializeObject.
What is the best way to serialize the object including all the Interface properties?

I end up creating a custom contract resolver:
public class InterfaceContractResolver : DefaultContractResolver
{
private readonly Type[] _interfaceTypes;
private readonly ConcurrentDictionary<Type, Type> _typeToSerializeMap;
public InterfaceContractResolver(params Type[] interfaceTypes)
{
_interfaceTypes = interfaceTypes;
_typeToSerializeMap = new ConcurrentDictionary<Type, Type>();
NamingStrategy = new CamelCaseNamingStrategy();
}
protected override IList<JsonProperty> CreateProperties(
Type type,
MemberSerialization memberSerialization)
{
var typeToSerialize = _typeToSerializeMap.GetOrAdd(
type,
t => _interfaceTypes.FirstOrDefault(
it => it.IsAssignableFrom(t)) ?? t);
var props = base.CreateProperties(typeToSerialize, memberSerialization);
// mark all props as not ignored
foreach (var prop in props)
{
prop.Ignored = false;
}
return props;
}
}
internal static class JsonConvertExtension
{ {
public static JsonSerializerSettings GetCamelCaseSettings() public static JsonSerializerSettings SetupJsonSerializerSettings(Type eventType)
{ {
var eventTypes = eventType.GetParentTypes().ToArray();
return new JsonSerializerSettings return new JsonSerializerSettings
{ {
ContractResolver = new DefaultContractResolver ContractResolver = new InterfaceContractResolver(eventTypes),
{ Formatting = Formatting.Indented,
NamingStrategy = new CamelCaseNamingStrategy() TypeNameHandling = TypeNameHandling.Auto,
},
Formatting = Formatting.Indented
}; };
And calling it using the type:
var message = JsonConvert.SerializeObject(#event, JsonConvertExtension.SetupJsonSerializerSettings(#event.GetType()));

Related

System.Reflection.TargetException (Object does not match target type)

I get System.Reflection.TargetException (Object does not match target type) in prop.GetValue(t).
The method receives an array of JSON objects and I use the ParamsQuery class as DTO to match the data. I need to get the value that properties 2-6 contain to concatenate a string.
Thgank you
public async Task<ActionResult<IEnumerable<Ficha>>> GetFichas(List<ParamsQuery> paramsQuery)
{
foreach (ParamsQuery pq in paramsQuery)
{
Type t = pq.GetType();
PropertyInfo[] propsInfo = t.GetProperties();
foreach (var prop in propsInfo)
{
var propVal = prop.GetValue(t);
...
}
}
return await ...
}
public class ParamsQuery
{
public string Campo { get; set; }
public string Operador { get; set; }
public string Criterio1 { get; set; }
public string Criterio2 { get; set; }
public string Criterio3 { get; set; }
public string Criterio4 { get; set; }
public string Criterio5 { get; set; }
}
Change it like below:
foreach (ParamsQuery pq in paramsQuery)
{
Type t = pq.GetType();
PropertyInfo[] propsInfo = t.GetProperties();
foreach (var prop in propsInfo)
{
var propVal = prop.GetValue(pq);
...
}
}

Why is my Viewmodel that I am returning to my POST method in MVC returning a null ViewModel

I really thought I understood what I was doing, but here goes. I decided I need more properties in my View so I created a ViewModel called ViewDataset and replaced the original Dataset model in the View. On my HttpGet TestDataset the View Model is populated correctly with data from the Viewmodel:
My original dataset that was working is below:
public class Dataset
{
public int Dataset_ID { get; set; }
public int Category_ID { get; set; }
public string Provider { get; set; }
public string Name { get; set; }
public string Institution { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public bool Domestic { get; set; }
public bool International { get; set; }
public bool Includes_PHI { get; set; }
public bool PHI_Limited { get; set; }
public bool Includes_PIL { get; set; }
public bool PIL_Limited { get; set; }
public bool Citation_Requirements { get; set; }
public string Citation_Comments { get; set; }
public Nullable<System.DateTime> Availability_Beg_DT { get; set; }
public Nullable<System.DateTime> Availability_End_DT { get; set; }
public bool Subscription_Renewal { get; set; }
public Nullable<System.DateTime> Subscription_Renewal_DT { get; set; }
public bool Retention_Expiry { get; set; }
public Nullable<System.DateTime> Retention_Expiry_DT { get; set; }
public bool Data_Destruction { get; set; }
public Nullable<System.DateTime> Data_Destruction_DT { get; set; }
public string Data_Destruction_Instructions { get; set; }
public Nullable<int> Contract_ID { get; set; }
public bool Draft_Status { get; set; }
public bool Admin_Only { get; set; }
public string Dataset_Alias { get; set; }
public bool Loc_Amazon { get; set; }
public bool Loc_IT_Research { get; set; }
public bool Loc_Research_Proj { get; set; }
public bool Loc_Secure_Data { get; set; }
public bool Loc_Mercury { get; set; }
public bool Loc_Research_CC { get; set; }
public bool Loc_Research_VM { get; set; }
public bool Loc_External { get; set; }
public bool Access_Url { get; set; }
public bool Access_Download_App { get; set; }
public bool Access_Lab_Terminal { get; set; }
public bool Access_Email_Req { get; set; }
public bool Access_File_Download { get; set; }
public bool Access_Other { get; set; }
public string Location_Notes { get; set; }
public string Access_Notes { get; set; }
public Nullable<System.DateTime> Date_Added { get; set; }
public Nullable<System.DateTime> Date_Modified { get; set; }
public string Added_By_User { get; set; }
public string Updated_By_User { get; set; }
public bool External_Collaborators
{
get; set;
}
}
Here is my new ViewDataset (viewmodel)
public class ViewDataset
{
public Dataset dataset;
public List<SelectListItem> categories_list;
}
Here is my is a sample of my view ... couple of lines, but the view is populated correctly with data from the ViewDataset model
#model ResearchDataInventoryWeb.Models.ViewDataset
<td>
#Html.TextBoxFor(model => model.dataset.Institution, new { placeholder = "<Institution>", #class = "input-box" })
</td>
<td>
#Html.TextBoxFor(model => model.dataset.Name, new { placeholder = "<Dataset Name>", #class = "input-box" })
</td>
#Html.CheckBoxFor(model => model.dataset.Domestic) <span class="display-checkbox">Domestic</span>
#Html.CheckBoxFor(model => model.dataset.International) <span class="display-checkbox">International</span>
#Html.CheckBoxFor(model => model.dataset.Includes_PHI) <span class="display-checkbox">Includes PHI </span>
#Html.CheckBoxFor(model => model.dataset.Includes_PIL) <span class="display-checkbox">Includes PII </span><br />
#Html.CheckBoxFor(model => model.dataset.External_Collaborators) <span class="display-checkbox">External Collaborators Allowed (Sharable)</span>
#Html.CheckBoxFor(model => model.dataset.Citation_Requirements) <span class="display-checkbox">Citation Requirements</span>
<input type="submit" value="TestPost" />
Here is my HttpPost TestDataset : The model Viewdataset (dataset, categories_list) properties that I am passing back from the view are NULL, am I missing something?
[HttpPost]
public ActionResult TestDataset(ViewDataset viewdataset)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:4251/");
//HTTP POST
viewdataset.dataset.Category_ID = 2;
viewdataset.dataset.Dataset_ID = 1;
viewdataset.dataset.Location = "Fingers Crossed";
viewdataset.dataset.Institution = "Not Null";
var dataset = viewdataset.dataset;
// var postTask = client.PostAsJsonAsync<Dataset>("api/datasets/1", dataset);
var postTask = client.PostAsJsonAsync("api/datasets/1", dataset);
postTask.Wait();
var result = postTask.Result;
if (result.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
}
ModelState.AddModelError(string.Empty, "Server Error. Please contact administrator.");
return View(viewdataset);
}
Here if my HttpGet TestDataset in case you need to see it
public async Task<ActionResult> TestDataset()
{
//Hosted web API REST Service base url
string Baseurl = "http://localhost:4251/";
List<Dataset> dataset = new List<Dataset>();
List<Category> categories = new List<Category>();
var parm = "1";
var returned = new Dataset();
var ViewDataset = new ViewDataset();
using (var client = new HttpClient())
{
//Passing service base url
client.BaseAddress = new Uri(Baseurl);
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Sending request to find web api REST service resource GetAllEmployees using HttpClient
HttpResponseMessage Res = await client.GetAsync("api/Datasets/" + parm);
HttpResponseMessage Res2 = await client.GetAsync("api/Categories");
//Checking the response is successful or not which is sent using HttpClient
if (Res.IsSuccessStatusCode)
{
//Storing the response details recieved from web api
var EmpResponse = Res.Content.ReadAsStringAsync().Result;
//Deserializing the response recieved from web api and storing into the Employee list
returned = JsonConvert.DeserializeObject<Dataset>(EmpResponse);
}
if (Res2.IsSuccessStatusCode)
{
//Storing the response details recieved from web api
var CatResp = Res2.Content.ReadAsStringAsync().Result;
//Deserializing the response recieved from web api and storing into the Employee list
categories = JsonConvert.DeserializeObject<List<Category>>(CatResp);
}
// Create the Categories Select List
var categoryList = new List<SelectListItem>();
foreach (Category c in categories)
{
categoryList.Add(new SelectListItem
{
Value = c.Category_ID.ToString(),
Text = c.Description,
Selected = (c.Category_ID == returned.Category_ID ? true : false)
});
}
ViewDataset.dataset = returned;
ViewDataset.categories_list = categoryList;
//returned.External_Collaborators = returned.External_Collaborators == null || false ? false : true;
//returning the employee list to view
return View(ViewDataset);
}
}
Try to change
public class ViewDataset
{
public Dataset dataset;
public List<SelectListItem> categories_list;
}
to
public class ViewDataset
{
public Dataset dataset { get; set; }
public List<SelectListItem> categories_list { get; set; }
}

How to avoid saving a value of property of form object when saving changes to db

In a crud asp.net core 2.2 web app, I need to avoid saving a property of form object to db. How do I do that?
I've tried using [Editable(false)] data annotation on the ListBin property to prevent saving property value to db.
[Table("supply_lists")]
public partial class SupplyLists
{
[Column("id")]
public int Id { get; set; }
[Column("category_id")]
public int CategoryId { get; set; }
[Required]
[Column("coursecode")]
[StringLength(200)]
public string Coursecode { get; set; }
[Required]
[Column("title")]
[StringLength(200)]
public string Title { get; set; }
[Required]
[Column("filename")]
[StringLength(200)]
public string Filename { get; set; }
[Column("isactive")]
public bool Isactive { get; set; }
[Column("date", TypeName = "smalldatetime")]
public DateTime Date { get; set; }
[Column("list_bin")]
public byte[] ListBin { get; set; }
[ForeignKey("CategoryId")]
[InverseProperty("SupplyLists")]
public virtual SupplyListCategory Category { get; set; }
}
[ModelMetadataType(typeof(MetaDataTypeModel))]
public partial class SupplyLists
{
}
public class MetaDataTypeModel
{
[Editable(false)]
public byte[] ListBin { get; set; }
[Display(Name = "Is Active")]
public bool Isactive { get; set; }
[Display(Name ="Course Code")]
public string Coursecode { get; set; }
[Display(Name = "Category")]
public int CategoryId { get; set; }
[DataType(DataType.Date)]
public DateTime Date { get; set; }
}
public class EditModel : PageModel
{
private readonly SupplyListCore22.Models.SupplyListsContext _context;
private readonly IHostingEnvironment _env;
public EditModel(SupplyListCore22.Models.SupplyListsContext context, IHostingEnvironment env)
{
_context = context;
_env = env;
}
[BindProperty]
public SupplyLists SupplyLists { get; set; }
[BindProperty]
public FileUpload FileUpload { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
SupplyLists = await _context.SupplyLists
.Include(s => s.Category).FirstOrDefaultAsync(m => m.Id == id);
if (SupplyLists == null)
{
return NotFound();
}
ViewData["CategoryId"] = new SelectList(_context.SupplyListCategory, "Id", "Category");
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
//if (!ModelState.IsValid)
//{
// return Page();
//}
_context.Attach(SupplyLists).State = EntityState.Modified;
await _context.SaveChangesAsync();
if (FileUpload.UploadSupplyList != null)
{
var fileUploadData = await utilities.utilities.ProcessFormFile(FileUpload.UploadSupplyList, ModelState);
if (ModelState.ErrorCount > 0)
{
ViewData["CategoryId"] = new SelectList(_context.SupplyListCategory, "Id", "Category");
return Page();
}
var sl = _context.SupplyLists.Find(SupplyLists.Id);
sl.ListBin = fileUploadData;
await _context.SaveChangesAsync();
}
return RedirectToPage("./Index");
}
It set the ListBin to null in db which is not what I wanted when saving changes (I wanted to preserve the old value of ListBin in db).

EF Core2.0 dbvalidation errors not displaying all errors

I've Created an EF core 2.0 application and trying to validate the model on Savechanges but its only returning the first validation error.
Here are my Dbcontext and controller
public partial class ProductWarehouseContext : DbContext
{ public List<string> ErrorList=new List<string>();
public ProductWarehouseContext(DbContextOptions<ProductWarehouseContext> options)
: base(options)
{
}
public virtual DbSet<Customer> Customer { get; set; }
public virtual DbSet<Order> Order { get; set; }
public virtual DbSet<OrderItem> OrderItem { get; set; }
public virtual DbSet<Product> Product { get; set; }
public virtual DbSet<Supplier> Supplier { get; set; }
public override int SaveChanges()
{
var entities = from e in ChangeTracker.Entries()
where e.State == EntityState.Added
|| e.State == EntityState.Modified
select e.Entity;
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
Validator.ValidateObject(
entity,
validationContext,
validateAllProperties: true);
}
return base.SaveChanges();}
}
Controller
[HttpPost]
public IActionResult Save([FromBody]CustomerViewModel customer)
{
using (var cont = _context.Database.BeginTransaction())
{
try
{
var cust = new Customer()
{
FirstName = customer.FirstName,
LastName = customer.LastName,
City = customer.City,
Country = customer.Country,
Phone = customer.Phone,
IsSubscribedforAlerts = customer.IsSubscribedforAlerts
};
_context.Customer.Add(cust);
_context.SaveChanges();
cont.Commit();
}
catch (Exception e)
{
Errors.Add(e.Message);
cont.Rollback();
foreach (var err in Errors)
{
ModelState.AddModelError("errors", err);
}
return Ok(ModelState);
}
}
return Ok();
}
Class
public partial class Customer
{
public Customer()
{
Order = new HashSet<Order>();
}
public int Id { get; set; }
[Required(ErrorMessage = "FirstName is required to save a new customer")]
public string FirstName { get; set; }
[Required(ErrorMessage = "LastName is required to save a new customer")]
public string LastName { get; set; }
public string City { get; set; }
public string Country { get; set; }
[Required(ErrorMessage = "PhoneNumber is required to save a new customer")]
public string Phone { get; set; }
public bool? IsSubscribedforAlerts { get; set; }
public ICollection<Order> Order { get; set; }
}
and error is only returnig ""firstname" is required and if I pass the firstname in request object then its returning "lastname" is required.
What should I do to return all the errors how we do it in EF6 using DbEntityValidationException ?
That's because ValidateObject() throws upon first encountering an error. Try using TryValidateObject() instead, and pass it a List<ValidationResult> that accumulate all errors.
Something like:
public class EntityValidationException : Exception
{
public EntityValidationException(IEnumerable<ValidationException> exceptions)
{
this.ValidationErrors = exceptions;
}
public IEnumerable<ValidationException> ValidationErrors { get; }
}
Then in your SaveChanges():
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(entity, validationContext, validationResults);
if (validationResults.Any())
throw new EntityValidationException(validationResults.Select(x => new ValidationException(x, null, null)));
}
Then in your controller/action, you can explicitly handle EntityValidationException:
catch (EntityValidationException validationException)
{
foreach (var err in validationException.ValidationErrors)
{
var validationResult = err.ValidationResult;
ModelState.AddModelError(validationResult.MemberNames.First(), validationResult.ErrorMessage);
}
}

How to filter audio podcasts from iTunes Search API?

By searching for podcasts with the iTunes API (http://www.apple.com/itunes/affiliates/resources/documentation/itunes-store-web-service-search-api.html) the result contains both audio and video podcasts. Is there any way to retrieve only audio podcasts from the API?
Thanks in advance :-)
From the documentation it doesn’t seem as if filtering audio and video podcasts is possible; however, you could loop through the resulting items and check whether each item is audio or video for filtering. You can do that be finding additional information from the RSS feed or by making another call to iTunes using the subscribePodcast url (see example).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Web.Script.Serialization;
using System.Xml.Linq;
using System.IO;
namespace ConsoleTest
{
class Program
{
static void Main(string[] args)
{
//Searching for Windows Weekly
string url = "https://itunes.apple.com/search?term=Windows%20Weekly&media=podcast&attibute=audio";
string json = FetchHTML(url);
JavaScriptSerializer s = new JavaScriptSerializer();
var result = s.Deserialize(json);
var audioOnly = new List();
foreach (var item in result.Results)
{
if (!IsVideo(item.TrackId))
{
audioOnly.Add(item);
}
}
foreach (var au in audioOnly)
{
Console.WriteLine(au.TrackName);
}
Console.ReadLine();
}
static bool IsVideo(string id)
{
string req = "https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/com.apple.jingle.app.finance.DirectAction/subscribePodcast?id=" + id + "&wasWarnedAboutPodcasts=true";
string xml = FetchHTML(req,true);
bool isVideo = false;
var re = XElement.Load(new StringReader(xml)).Elements("dict").Elements("dict");
bool next = false;
foreach (var e in re.Elements())
{
if (next)
{
//This is the is video value
isVideo = e.Name.LocalName.Trim().ToLower() == "true";
next = false;
break;
}
if (e.Value == "is-video")
{
next = true;
}
}
return isVideo;
}
static string FetchHTML(string url,bool doItAsITunes = false)
{
string htmlCode = "";
using (WebClient client = new WebClient())
{
if (doItAsITunes)
{
client.Headers.Add("user-agent", "iTunes/9.1.1");
}
htmlCode = client.DownloadString(url);
}
return htmlCode;
}
}
public class SearchResult
{
public SearchResult()
{
Results = new List();
}
public int ResultCount { set; get; }
public List Results { set; get; }
}
public class Item
{
public Item()
{
GenreIDs = new List();
Genres = new List();
}
public string WrapperType { set; get; }
public string Kind { set; get; }
public string ArtistID { set; get; }
public string CollectionID { set; get; }
public string TrackId { set; get; }
public string ArtistName { set; get; }
public string CollectionName { set; get; }
public string TrackName { set; get; }
public string CollectionCensoredName { set; get; }
public string TrackCensoredName { set; get; }
public string ArtistViewUrl { set; get; }
public string FeedUrl { set; get; }
public string TrackViewUrl { set; get; }
public string PreviewUrl { set; get; }
public string ArtworkUrl60 { set; get; }
public string ArtworkUrl100 { set; get; }
public float CollectionPrice { set; get; }
public float TrackPrice { set; get; }
public string CollectionExplicitness { set; get; }
public string TrackExplicitness { set; get; }
public string DiscCount { set; get; }
public string DiscNumber { set; get; }
public string TrackCount { set; get; }
public string TrackNumber { set; get; }
public string TrackTimeMillis { set; get; }
public string Country { set; get; }
public string Currency { set; get; }
public string PrimaryGenreName { set; get; }
public List GenreIDs { set; get; }
public List Genres { set; get; }
}
}
Yes. In regular serach you get everything:
https://itunes.apple.com/search?term=jack+johnson
But you can add some params to request for example (for your case)
&entity=song
So the request will be:
https://itunes.apple.com/search?term=jack+johnson&entity=song
For more look at Searching seaction in this docs