Deserialize into object with variable name - c#

I hope someone can help me with this; I have just started working on a website which requires to make API calls. I use an open source library for the API calls. Most of the calls work great, but I can't get the most important one to work. The json string when deserialized returns an empty object.
JSON String:
{"benbeun":{"id":27266833,"name":"BenBeun","profileIconId":25,"summonerLevel":30,"revisionDate":1393655593000}}
Call, where responseText is the above JSON string:
public static T CreateRequest(string url)
{
var result = new T();
var getRequest = (HttpWebRequest)WebRequest.Create(url);
using (var getResponse = getRequest.GetResponse())
using (var reader = new System.IO.StreamReader(getResponse.GetResponseStream()))
{
var responseText = reader.ReadToEnd();
result = JsonConvert.DeserializeObject<T>(responseText);
}
return result;
}
Default class from the library:
public class SummonerDto
{
/// <summary>
/// Summoner ID.
/// </summary>
[JsonProperty("id")]
public long Id { get; set; }
/// <summary>
/// Summoner name.
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// ID of the summoner icon associated with the summoner.
/// </summary>
[JsonProperty("profileIconId")]
public int ProfileIconId { get; set; }
/// <summary>
/// Date summoner was last modified specified as epoch milliseconds.
/// </summary>
[JsonProperty("revisionDate")]
public long RevisionDate { get; set; }
/// <summary>
/// Summoner level associated with the summoner.
/// </summary>
[JsonProperty("summonerLevel")]
public long SummonerLevel { get; set; }
}
I can get the class below to work in my calls; however the 'benbeun' string is variable, so this class cannot be used.
public class Benbeun
{
public int id { get; set; }
public string name { get; set; }
public int profileIconId { get; set; }
public int summonerLevel { get; set; }
public long revisionDate { get; set; }
}
public class SummonerDto
{
public Benbeun benbeun { get; set; }
}
Any pointers? I already tried numerous options provided in other questions, but I fear my knowledge is lacking in where exactly my problem lies. I feel I am close with the code below, however it returns an empty object aswell.
public class SummonerDto
{
public IDictionary<string, Summoner> Summoner { get; set; }
}
public class Summoner
{
/// <summary>
/// Summoner ID.
/// </summary>
[JsonProperty("id")]
public long Id { get; set; }
/// <summary>
/// Summoner name.
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// ID of the summoner icon associated with the summoner.
/// </summary>
[JsonProperty("profileIconId")]
public int ProfileIconId { get; set; }
/// <summary>
/// Date summoner was last modified specified as epoch milliseconds.
/// </summary>
[JsonProperty("revisionDate")]
public long RevisionDate { get; set; }
/// <summary>
/// Summoner level associated with the summoner.
/// </summary>
[JsonProperty("summonerLevel")]
public long SummonerLevel { get; set; }
}

Use Dictionary<string,Summoner> to deserialize.
var obj = JsonConvert.DeserializeObject<Dictionary<string,Summoner>>(json);

Related

Newtonsoft Json serialize dynamic property back to respective T Type

I have a class that I am converting into a json string and saving. The class contains 2 dynamic properties which are of some type T. In my case its LdrEntity. Now when I am deserializing and getting the object back, it is assigning a json string to that dynamic property. Is there a way to specify to serilialize the dynamic property back to LdrEntity instead of the json string. Please help.
Here is my class (Please see LdrReagentEntity and BciReagentEntity properties)
using Domain.Model.Shared.Entity.Panel;
using System.Collections.Generic;
namespace Authoring.DataAccess.DataModel
{
/// <summary>
/// Cocktail reagent structure to coordinate data.
/// </summary>
public class CocktailReagent
{
/// <summary>
/// Id.
/// </summary>
public string Id { get; set; }
/// <summary>
/// Reagent type.
/// </summary>
public ReagentType Type { get; set; }
/// <summary>
/// Reagent name.
/// </summary>
public string ActualName { get; set; }
/// <summary>
/// Reagent name.
/// </summary>
public int Version { get; set; }
/// <summary>
/// Stability days.
/// </summary>
public int StabilityDays { get; set; }
/// <summary>
/// Number of tests.
/// </summary>
public int NumberOfTests { get; set; }
/// <summary>
/// Reagent comments.
/// </summary>
public string Comments { get; set; }
/// <summary>
/// Cocktail reagent Items.
/// </summary>
public List<CocktailReagentItem> SelectedReagents { get; set; }
}
public class CocktailReagentItem
{
public string Id { get; set; }
public uint Index { get; set; }
public uint Volume { get; set; }
public dynamic LdrReagentEntity { get; set; } //The original type is LdrEntity
public dynamic BciReagentEntity { get; set; } //The original type is LdrEntity
}
}
The two dynamic properties when converted are of type LdrEntity.
So when I convert to json string, I do the following.
public class LdrExportObj
{
public List<CocktailReagent> CocktailReagents { get; set; }
public string HashCode { get; set; }
}
public void Export()
{
var exportObj = new LdrExportObj
{
CocktailReagents = listOfCocktailReagents, //These are the list of items I am saving
HashCode = HashCodeHelper.GenerateHashCode(str),
};
var exportStr = JsonConvert.SerializeObject(exportObj, Formatting.Indented); //I get the json string and save it.
//Save to file
}
When I Import I say
public void Import()
{
var obj = JsonConvert.DeserializeObject<LdrExportObj>(str);
var cocktailsStr = JsonConvert.SerializeObject(obj.CocktailReagents, Formatting.Indented); //If I expand the object and see the dynamic properties, its a json string.
return new LdrExportObj()
{
CocktailReagents = obj.CocktailReagents,
HashCode = obj.HashCode,
};
}
When I debug, its not serializing the internal json string back to LdrEntity, instead its assigning a json string to the dynamic property, See the BciReagentEntity Property in the screenshot. I want it to be conveted back to the original LdrEntity type.

Why IFormFile is null in nested object?

I've got these models
public sealed class UpdateFacilityReportFirstStepCommand : ICommand<ExecutionResult>
{
// other props
/// <summary>
/// Gets or sets the hired staff.
/// </summary>
/// <value>The hired staff.</value>
public List<HiredStaffUpsertModel> HiredStaff { get; set; } = new List<HiredStaffUpsertModel>();
}
public class HiredStaffUpsertModel
{
// other props
/// <summary>
/// Gets or sets the insurance card file.
/// </summary>
/// <value>The insurance card file.</value>
public HiredStaffCarInsuranceCardModel CarInsuranceCardFile { get; set; } = new HiredStaffCarInsuranceCardModel();
}
public class HiredStaffCarInsuranceCardModel
{
/// <summary>
/// Gets or sets the file.
/// </summary>
/// <value>The file.</value>
[FileValidator("pdf,doc,docx", 10 * 1024 * 1024, true)]
public IFormFile File { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is changed.
/// </summary>
/// <value><c>true</c> if this instance is changed; otherwise, <c>false</c>.</value>
public bool IsChanged { get; set; }
}
And in my controller I'm expecting public [FromForm] UpdateFacilityReportFirstStepCommand command.
That's how I send form in Postman (content-type: multipart/form-data):
And that's what I get:
I have no idea why my File is null, although bool IsChanged is received. My frontend developer said that he'll send me form keys like on the Postman screenshot, and I don't get why it works fine with primitive types and doesn't with files.
The way I found to solve it, it's same as yours.
public class Purchase
{
public string Customer { get; set; }
public IEnumerable<Product> Products { get; set; }
}
public class Product
{
public string ProductCode { get; set; }
public IFormFile ProductImage { get; set; }
}
You have to send it like Products[0].ProductImage, otherwise you'll get null in ProductImage.

Implementing history tracking in SaveChanges override

I am currently trying to implement history tracking on all of my tables in my app in a generic way by overriding the SaveChanges method and making use of reflection. As a simple case, let's say I have 2 classes/dbsets for my domain objects and a history table for each like the following:
DbSet<Cat> Cats { get; set; }
DbSet<CatHistory> CatHistories { get; set; }
DbSet<Dog> Dogs { get; set; }
DbSet<DogHistory> DogHistories { get; set; }
The CatHistory class looks like the following (DogHistory follows the same scheme):
public class CatHistory : HistoricalEntity
{
public int CatId { get; set; }
public virtual Cat Cat{ get; set; }
}
My Goal is when an object is saved, I would like to insert a record in the appropriate history table. I am having trouble overcoming type difference when using reflection. My current attempt is below and I seem to be stuck on the //TODO: line:
var properties = entry.CurrentValues.PropertyNames.Where(x => entry.Property(x).IsModified).ToList();
//get the history entry type from our calculated typeName
var historyType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(x => x.Name == historyTypeName);
if(historyType != null)
{
//modified entries
if (dbSet != null && historyDbSet != null && entry.State == EntityState.Modified)
{
var existingEntry = dbSet.Find(entry.Property("Id").CurrentValue);
//create history record and add entry to table
var newHistories = GetHistoricalEntities(existingEntry, type, entry);
var listType = typeof(List<>).MakeGenericType(new[] { historyType });
var typedHistories = (IList)Activator.CreateInstance(listType);
//TODO: turn newHistories (type = List<HistoricalEntity>) into specific list type (List<MyObjectHistory>) so I can addrange on appropriate DbSet (MDbSet<MyObjectHistory>)
historyDbSet.AddRange(newHistories);
}
}
You could use AutoMapper to map your historical entities. I just created a little test, hopefully it replicates your situation:
IList dogs = new List<Dog>() { new Dog { Id = 1, Name = "Alsatian" }, new Dog { Id = 2, Name = "Westie" } };
var dogHistoryType = typeof(DogHistory);
var listType = typeof(List<>).MakeGenericType(new[] { dogHistoryType });
var typedHistories = (IList)Activator.CreateInstance(listType);
mapper.Map(dogs, typedHistories);
foreach (var historyItem in typedHistories)
{
this.Add(historyItem);
}
I will try to explain the way I have implemented in my application.
I have created Models with name ending History for models for which application needs to insert before deleting the record from the original table.
BaseModel.cs
namespace ProductVersionModel.Model
{
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
/// <summary>
/// all common properties of the tables are defined here
/// </summary>
public class BaseModel
{
/// <summary>
/// id of the table
/// </summary>
[Key]
public int Id { get; set; }
/// <summary>
/// user id of the user who modified last
/// </summary>
public string LastModifiedBy { get; set; }
/// <summary>
/// last modified time
/// </summary>
public DateTime LastModifiedTime { get; set; }
/// <summary>
/// record created user id
/// </summary>
[Required]
public string CreatedBy { get; set; }
/// <summary>
/// record creation time
/// </summary>
public DateTime CreationTime { get; set; }
/// <summary>
/// Not mapped to database, only for querying used
/// </summary>
[NotMapped]
public int RowNumber { get; set; }
}
}
Product.cs
namespace ProductVersionModel.Model
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
/// <summary>
/// store detals of the product
/// </summary>
public class ProductStatus : BaseModel
{
/// <summary>
/// Name of the product
/// </summary>
[Required, MaxLength(100)]
public string Name { get; set; }
/// <summary>
/// product version validity start date
/// </summary>
public DateTime ValidFrom { get; set; }
/// <summary>
/// product version valid till
/// </summary>
public DateTime? ValidTill { get; set; }
/// <summary>
/// This field used to keep track of history of a product
/// </summary>
public int ProductNumber { get; set; }
}
}
HistoryBaseModel.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ProductVersionModel.Model.History
{
public class HistroyBaseModel
{
/// <summary>
/// id of the table
/// </summary>
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string DeletedBy { get; set; }
public DateTime? DeletedTime { get; set; }
/// <summary>
/// record created user id
/// </summary>
[Required]
public string CreatedBy { get; set; }
/// <summary>
/// record creation time
/// </summary>
public DateTime CreationTime { get; set; }
}
}
ProductStatusHistory.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using ProductVersionModel.Model.History;
// ReSharper disable once CheckNamespace
namespace ProductVersionModel.Model.History
{
public class ProductStatusHistory : HistroyBaseModel
{
/// <summary>
/// Name of the product
/// </summary>
[MaxLength(100)]
public string Name { get; set; }
/// <summary>
/// product version validity start date
/// </summary>
public DateTime ValidFrom { get; set; }
/// <summary>
/// product version valid till
/// </summary>
public DateTime? ValidTill { get; set; }
/// <summary>
/// This field used to keep track of history of a product
/// </summary>
public int ProductNumber { get; set; }
}
}
In Delete method of your CrudRepository
public virtual int Delete(List<object> ids, string userName)
{
try
{
foreach (var id in ids)
{
var dbObject = _table.Find(id);
HistroyBaseModel historyRecord = null;
var modelAssembly = Assembly.Load(nameof(ProductVersionModel));
var historyType =
modelAssembly.GetType(
// ReSharper disable once RedundantNameQualifier - dont remove namespace it is required
$"{typeof(ProductVersionModel.Model.History.HistroyBaseModel).Namespace}.{typeof(TModel).Name}History");
if (historyType != null)
{
var historyObject = Activator.CreateInstance(historyType);
historyRecord = MapDeletingObjectToHistoyObject(dbObject, historyObject, userName);
DatabaseContext.Entry(historyRecord).State = EntityState.Added;
}
DatabaseContext.Entry(dbObject).State = EntityState.Deleted;
}
return DatabaseContext.SaveChanges();
}
catch (DbUpdateException ex)
{
throw HandleDbException(ex);
}
}
protected virtual HistroyBaseModel MapDeletingObjectToHistoyObject(object inputObject, object outputObject, string userName)
{
var historyRecord = MapObjectToObject(inputObject, outputObject) as HistroyBaseModel;
if (historyRecord != null)
{
historyRecord.DeletedBy = userName;
historyRecord.DeletedTime = DateTime.UtcNow;
}
return historyRecord;
}
protected virtual object MapObjectToObject(object inputObject, object outputObject)
{
var inputProperties = inputObject.GetType().GetProperties();
var outputProperties = outputObject.GetType().GetProperties();//.Where(x => !x.HasAttribute<IgnoreMappingAttribute>());
outputProperties.ForEach(x =>
{
var prop =
inputProperties.FirstOrDefault(y => y.Name.Equals(x.Name) && y.PropertyType == x.PropertyType);
if (prop != null)
x.SetValue(outputObject, prop.GetValue(inputObject));
});
return outputObject;
}
Where TModel is the type of the model
public class CrudRepository<TModel> : DataAccessBase, ICrudRepository<TModel> where TModel : class, new()
public class ProductStatusRepository : CrudRepository<ProductStatus>, IProductStatusRepository
You can override methods MapDeletingObjectToHistoyObject and MapObjectToObject in your related repository if you want to map complex entitites like child element list.

I cannot retrieve my saved data from database using Entity Framework and I'm not getting any errors

I have the following code, and am trying to retrieve the data from my database. I know the data is saving correctly, but what am I doing wrong when calling it? I use the SQL Server Object Window within Visual Studio and can see that there is information in the database in the Song table, but when I try to call it in my action method, I do not see any information being retrieved when a breakpoint is set on the .ToList() method.
MusicController.cs
SongDatabase db = new SongDatabase();
[HttpGet]
public ActionResult Music()
{
var songs = db.Songs.ToList();
return View(songs);
}
Song.cs
/// <summary>
/// This is the song of the music for the playlist, and will be logged into the Music Manager app.
/// </summary>
public class Song
{
/// <summary>
/// The Id of the song.
/// </summary>
public int SongId { get; set; }
/// <summary>
/// The name of the song.
/// </summary>
[Required]
[DisplayName("Song")]
public string SongName { get; set; }
/// <summary>
/// The artist of the song.
/// </summary>
[Required, StringLength(100)]
[DisplayName("Artist")]
public string ArtistName { get; set; }
/// <summary>
/// Represents an album from an artist.
/// </summary>
[Required, StringLength(50)]
[DisplayName("Album")]
public string AlbumName { get; set; }
/// <summary>
/// The duration of the song.
/// </summary>
public double Duration { get; set; }
/// <summary>
/// Whether or not this song should be excluded when calculating the total duration of the current playlist.
/// </summary>
public bool Exclude { get; set; }
}
SongDatabase.cs
public class SongDatabase : DbContext
{
public DbSet<Song> Songs { get; set; }
public SongDatabase()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<SongDatabase>());
}
}

My .saveChanges() method is not saving because it is saying an object reference is required for the non-static field, method, or property

I am getting an error when I call the .SaveChanges(). It is telling me an object reference is required for the non-static field, method, or property 'SongDatabase.Songs'. Here is my code for my controller. Can someone tell me why this error is occurring?
[HttpPost]
public ActionResult Add(Song song)
{
using (SongDatabase db = new SongDatabase())
{
SongDatabase.Songs.Add(song);
SongDatabase.SaveChanges();
}
return RedirectToAction("Music");
}
Here is my code for my database for the songs.
public class SongDatabase : DbContext
{
public DbSet<Song> Songs { get; set; }
public DbSet<Artist> Artists { get; set; }
public DbSet<Album> Albums { get; set; }
public SongDatabase()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<SongDatabase>());
}
}
And lastly, here is the code for my Song class.
public class Song
{
public Song()
{
Album = new List<Album>();
}
/// <summary>
/// The Id of the song.
/// </summary>
public int SongId { get; set; }
/// <summary>
/// The name of the song.
/// </summary>
public string SongName { get; set; }
/// <summary>
/// The artist of the song.
/// </summary>
public int ArtistId { get; set; }
/// <summary>
/// The duration of the song.
/// </summary>
public double Duration { get; set; }
/// <summary>
/// Whether or not this song should be excluded when calculating the total duration of the current playlist.
/// </summary>
public bool Exclude { get; set; }
/// <summary>
/// Navigation property linking the album class to the song class.
/// </summary>
public virtual ICollection<Album> Album { get; set; }
/// <summary>
/// Navigation property linking the artist class to the song class.
/// </summary>
public virtual Artist Artist { get; set; }
}
You're trying to access public DbSet<Song> Songs { get; set; } as a static value, in your code here
[HttpPost]
public ActionResult Add(Song song)
{
using (SongDatabase db = new SongDatabase())
{
SongDatabase.Songs.Add(song);
SongDatabase.SaveChanges();
}
return RedirectToAction("Music");
}
You create an instance of SongDatabase with SongDatabase db = new SongDatabase() but you're not using that instance db. You should change it for
[HttpPost]
public ActionResult Add(Song song)
{
using (SongDatabase db = new SongDatabase())
{
db.Songs.Add(song);
db.SaveChanges();
}
return RedirectToAction("Music");
}

Categories