I'm quite new about MVC. I have the following Model classes:
public class Store
{
public PriceList PriceListInfo { get; set; }
public IStore storeData;
}
public class PriceList
{
public int id { get; set; }
public string codice { get; set; }
}
public interface IStore
{
[...]
}
public class Silo2Store : IStore
{
public int S2 { get; set; }
public int S3 { get; set; }
}
And i want use this model in my view:
#model Store
#Html.TextBoxFor(model => ((Silo2Store)Model.storeData).S3)
The corresponding Controller method is:
public ActionResult Customer()
{
using (Store t = (Store)Session["Store"])
{
if (t.PriceListInfo == null)
{
t.PriceListInfo = new PriceList();
}
t.PriceListInfo.codice = "XXX";
return View(t);
}
}
And I'd like to retrieve the model in my Controller:
[HttpPost]
public ActionResult Customer(Store modelStore)
{
var test = ((Silo2Store)Model.storeData).S3;
}
but Model.storeData attribute isn't initialized in my view, it's null. Then, I can't retrieve the value in my controller.
Should I change my model in anyway?
You have to define your own model binder for IStore.
Taken from this article on MSDN Magazine about MVC Model Binding:
For example, even though the Microsoft .NET Framework provides excellent support for object-oriented principles, the DefaultModelBinder offers no support for binding to abstract base classes and interfaces.
Related
I'm using last version of json implementation in Pomelo 5.0 and configure my maria server to use microsoft json serialisation.
var serverVersion = new MariaDbServerVersion(new Version(10, 3, 0));
services.AddDbContext<BusinessManagementDbContext>(options =>
{
options.UseMySql(databaseConfiguration.ConnectionString, serverVersion, m =>
{
m.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically);
m.EnableRetryOnFailure();
});
options.EnableSensitiveDataLogging(true);
});
I can save my POCO in my db but when I try to query my data, I get a null object.
Here's my data :
HeidySQL data
My query is pretty simple but I think I'm not using the right way for json query.
await Context.ValidatedSaleInvoices.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id);
It seems like there is no deserialization between my data and my property "Content".
How can I do this ?
Thank you,
Edit
My model is :
public class ValidateSaleInvoiceEntity
{
public int Id { get; set; }
public ValidateSaleInvoiceContent Content { get; set; }
}
public class ValidateSaleInvoiceContent
{
public string BusinessName { get; set; }
public DateTime Date { get; internal set; }
public string Number { get; internal set; }
public Address Address { get; internal set; }
public List<ValidateSaleInvoiceLineEntity> Lines { get; internal set; } = new List<ValidateSaleInvoiceLineEntity>();
}
public class ValidateSaleInvoiceLineEntity
{
public string Description { get; internal set; }
public decimal Quantity { get; internal set; }
public decimal UnitPriceVatExcluded { get; internal set; }
public decimal VatRate { get; internal set; }
}
And my json Result was like this (empty, like there waere no deserialisation: { "BusinessName":"", "Date":"", "Number":"" etc.}
My boss stop my poc about MariaDB Json implementation so I had to go back to this good old friend pure sql column :/ . That's why I haven"t a full json result. Sorry
For a property to serialize/deserialize JSON automatically to a POCO, you need to tell Pomelo, that the table column of the property is of the MySQL/MariaDB type json:
public class ValidateSaleInvoiceEntity
{
public int Id { get; set; }
[Column(TypeName = "json")] // <-- this is one way to do it
public ValidateSaleInvoiceContent Content { get; set; }
}
public class MyContext : DbContext
{
// ...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ValidateSaleInvoiceEntity>()
.Property(e => e.Content)
.HasColumnType("json"); // <-- this is another way to do it
}
}
Here is a fully working console project:
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
// EF Core entities:
public class IceCream
{
public int IceCreamId { get; set; }
public string Name { get; set; }
// Either use this data annotation, or the corresponding Fluent API call (see
// OnModelCreating), to explicitly mark the column type as JSON.
[Column(TypeName = "json")]
public IceCreamDetails Details { get; set; }
}
// JSON class:
public class IceCreamDetails
{
public int Kilojoule { get; set; }
public int Rating { get; set; }
}
public class Context : DbContext
{
public DbSet<IceCream> IceCreams { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
var connectionString = "server=127.0.0.1;port=3306;user=root;password=;database=So68020732";
var serverVersion = ServerVersion.AutoDetect(connectionString);
optionsBuilder.UseMySql(connectionString, serverVersion, options => options
.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically))
.UseLoggerFactory(
LoggerFactory.Create(
configure => configure
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IceCream>(
entity =>
{
// Either use this Fluent API call, or the corresponding data annotation
// (see the IceCreamDetails class), to explicitly mark the column type as JSON.
entity.Property(e => e.Details)
.HasColumnType("json");
entity.HasData(
new IceCream {IceCreamId = 1, Name = "Vanilla", Details = new IceCreamDetails { Kilojoule = 865, Rating = 9 }},
new IceCream {IceCreamId = 2, Name = "Chocolate", Details = new IceCreamDetails { Kilojoule = 903, Rating = 10 }});
});
}
}
internal static class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var iceCreams = context.IceCreams
.OrderBy(i => i.IceCreamId)
.ToList();
Trace.Assert(iceCreams.Count == 2);
Trace.Assert(iceCreams[0].Details.Kilojoule == 865);
Trace.Assert(iceCreams[1].Details.Rating == 10);
}
}
}
You can find the most comprehensive JSON sample code on our repository (see the JSON mapping and query scenarios section).
I am using an enumeration class like this:
public class BillTransactionState
{
public static readonly BillTransactionState Initialized = new BillTransactionState(1, "Initialized");
public static readonly BillTransactionState Invoiceable = new BillTransactionState(2, "Invoiceable");
public static readonly BillTransactionState NoSow = new BillTransactionState(3, "NoSow");
public static readonly BillTransactionState Invoiced = new BillTransactionState(4, "Invoiced");
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
I have another class that references BillTransactionState:
public class BillTransactionStateHistory : IEntity<long>
{
public long Id { get; set; }
public DateTime EffectiveDate { get; set; }
public BillTransactionState BillTransactionState { get; set; }
}
In my webapi, when I do something like this:
var billTransactionStateHistory = new BillTransactionStateHistory ({
BillTransaction = BillTransaction .Initialized,
EffectiveDate = DateTime.Now
});
_dbContext.BillTransactionStateHistories.AddAsync(billTransactionStateHistory)
I get an error:
Identity insert is off for BillTransactionState.
I realized EF Core is trying to insert a row in BillTransactionState table with values 1 (for Id) and 'Initliazed'. How can I stop EF Core from trying to insert a row in this enumerated class. Any help regarding this will be highly appreciated. Thank you.
Write your BillTransactionStateHistory model class as follows:
public class BillTransactionStateHistory : IEntity<long>
{
public long Id { get; set; }
public DateTime EffectiveDate { get; set; }
public int BillTransactionState { get; set; }
}
Note: After that don't forget to update the database accordingly!
Then in the Web API Method:
var billTransactionStateHistory = new BillTransactionStateHistory ()
{
BillTransactionState = BillTransactionState.Initialized.Id,
EffectiveDate = DateTime.Now
};
_dbContext.BillTransactionStateHistories.AddAsync(billTransactionStateHistory)
You can see this answer on the official EF Core GitHub repository.
Your enumeration class and entity class would remain the same. You would have to introduce a ValueConverter or use PropertyBuilder.HasConverter to provide your mappings to and from BillTransactionState and int.
Lambda example:
// builder is IEntityTypeConfiguration<BillTransactionStateHistory>
builder.Property(e => e.BillTransactionState)
.HasColumnName("state")
.HasConversion(state => state.Id, stateId => SomeLookupMethod(stateId));
public BillTransactionState SomeLookupMethod(int id)
{
// TODO: Lookup transaction state based on id.
}
Please be aware that EF Core 2.x has issues with value converters and lambdas properly mapping to query expressions. EF Core 3.x has fixed this (related issue)
By default, Controller.Json generates JSON for each public member of a class. How can I change this so that some members are ignored. Please note that I am using .Net Core.
Example:
[HttpGet("/api/episodes")]
public IActionResult GetEpisodes()
{
var episodes = _podcastProvider.Get();
return Json(episodes);
}
Thanks.
You can use [JsonIgnore] attribute that available in Newtonsoft.Json namespace like below:
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
[JsonIgnore]
public int Age { get; set; }
}
How can I change this so that some members are ignored?
Under the covers this uses Newtonsoft.Json. There are two ways you can do this.
Use the JsonIgnore attribute and mark the properties you want omitted.
Have your episodes class define itself as "opt-in", meaning only properties marked with JsonProperty are serialized. [JsonObject(MemberSerialization.OptIn)]
It depends on the number of properties you need omitted versus serialized.
public class Episode
{
public int Id { get; }
[JsonIgnore] public string Name { get; }
[JsonIgnore] public Uri Uri { get; }
[JsonIgnore] public long Length { get; }
}
The above will yield the same JSON as this:
[JsonObject(MemberSerialization.OptIn)]
public class Episode
{
[JsonProperty]
public int Id { get; }
public string Name { get; }
public Uri Uri { get; }
public long Length { get; }
}
So it happens that you can prevent breeze json serialization of some properties using data annotations on your model by like this(well if you are using EF6 with JSON.NET on the backend)...
[Table("Project")]
public partial class Project
{
public Project()
{
}
public int id { get; set; }
[JsonIgnore]
public bool NoLongerExist { get; set; }
}
By doing so the property becomes invisible on this endpoint used by breeze
public IQueryable<Project> Projects()
{
return _db.Context.Projects.Where(o => o.NoLongerExist == true);
}
Can i apply [JsonIgnore] based on a certain condition like an authenticated user or a random if from this endpoint?
I have a self referencing model called Folder and also an Entity called Content which contains the Folder Entity.
public class Folder : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public virtual Folder Parent { get; set; }
public virtual ICollection<Folder> Children { get; set; }
}
public class Content : BaseEntity
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public string HTML { get; set; }
public string Summary { get; set; }
public int XmlConfigId { get; set; }
public int FolderId { get; set; }
public virtual Folder Folder { get; set; }
}
Here is my Application Db context
public class ApplicationDbContext: DbContext
{
public DbSet<Folder> Folders { get; set; }
public DbSet<Content> Contents { get; set; }
public ApplicationDbContext() : base("ProjectDB") {
Database.SetInitializer<ApplicationDbContext>(null);
}
}
Everything works fine if i am using a razor view to display the data and also i am able to access the The Folder property that is inside the Content Entity.
The problem is when i try to display the data using Web API.
My web API
public class ContentApiController : ApiController
{
[HttpGet]
public IEnumerable<Content> GetAllContents()
{
return _unitofwork.Contents.GetAllContents();
}
}
On the Web API, the GetAllContents() function just returns the Entity models coming directly from the Folders DBSet. It is not calling the ToList() function since i want to do lazy loading. Here is the code for the GetAllContents() function.
public IEnumerable<Content> GetAllContents()
{
return ApplicationDbContext.Contents.Include(c=>c.Folder);
}
So in order for this to work i have to add.
Configuration.LazyLoadingEnabled = false;
to my applicationDbContext constructor which i really don't want.
and also
Global.asax
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
WebApiConfig
JsonMediaTypeFormatter jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().Single();
jsonFormatter.UseDataContractJsonSerializer = false;
jsonFormatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
jsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
jsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.None;
Is there any way to expose the json data without out turning off Lazy loading. Thanks.
Just call ToList on your query, or, even better, ToListAsync:
[HttpGet]
public async Task<IEnumerable<Content>> GetAllContents()
{
return await _unitofwork.Contents.GetAllContents().ToListAsync;
}
Even if you enable LazyLoading, you cannot avoid to materialize your data before returning it to the client (and let the Serializer do its work).
In your MVC example, the framework itself enumerates the result in your View (I suppose), and thus you are not directly calling ToList, but in your scenario you have to materialize your Entities explicitly.
Please note that there is no performance issue in calling ToList/ToListAsync in your controller.