EF Core Enumeration class throwing exception - ef-core-2.1

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)

Related

EF Core and Pomelo 5.0 - how to query the json column

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).

Return a non-mapped Entity property based on a SUM of child records in the EntityFramework

Using an example, I have the following two Entities. The OrderEntity contains a collection of OrderLineEntites
public class OrderEntity
{
public string Reference { get; set; }
public string Description { get; set; }
public bool Confirmed { get; set; }
[NotMapped]
public int OrderLineCount { get; set; }
[InverseProperty("Order")]
public virtual ICollection<OrderLineEntity> OrderLineEntity__OrderEntity { get; set; }
}
public class OrderLineEntity
{
public string Name { get; set; }
public string Description { get; set; }
}
Using the following code I can load all the OrderLineEntities for all confirmed orders.
DbSet<OrderEntity> orderEntity.Where(x => x.Confirmed).Include(x => x.OrderLineEntity__OrderEntity)
What I need to do is set the non-mapped OrderLineCount property with the Count of the OrderLine records (to save actually loading them).
So for each loaded Order I have a fully populated Entity including the [NotMapped] property with an empty OrderLine collection.
Advise would be appreciated :)
Thanks
You can do this, but you have to change your approach. You have to manually map the objects yourself:
var query = from a in context.Orders.Where(x => x.Confirmed)
select new OrderEntity
{
Reference = a.Reference,
Description = a.Description,
Confirmed = a.Confirmed,
OrderLineCount = a.OrderLineEntity__OrderEntity.Count
};
return query.ToList();

Web API Json Serialization Exception & Dynamic Entities

I have a web API controller method that is returning an object which is giving the client a 500 internal server error. On the server, the output windows says "Newtonsoft.Json.JsonSerializationException". But I cannot see anything wrong with the class I am returning.. and I am sure this has historically been working. Any help would be greatly appreciated!
EDIT: Is this a problem with the web API not being able to serialize a 'dynamic entity'? The code that generates the class is here:
var id = User.Identity.GetUserId();
var user = db.Users
.Where(u => u.Id == id)
.Include(u => u.Friends)
.FirstOrDefault();
return user;
I am returning the following class;
public class User : IdentityUser
{
public User()
{
this.Friends = new List<UserFriend>();
}
public string PhoneNumber { get; set; }
public string Email { get; set; }
public List<UserFriend> Friends { get; set; }
public bool HasRegistered { get; set; }
public string LoginProvider { get; set; }
}
The 'UserFriend' class looks like this;
public class UserFriend
{
public int UserFriendId { get; set; }
public string Id { get; set; }
public string FriendUserId { get; set; }
public string FriendUserName { get; set; }
public string FriendPhoneNumber { get; set; }
}
Strangely, when I hover over the returned object on the server, the type is: {System.Data.Entity.DynamicProxies.User_7283E76A736B4DD47E89120E932CD5C04B62F84C316961F02CDAE3EEF4786504}. I am not sure what this is.. :-O
I used AutoMapper to create a DTO instead of just returning the User class. The DynamicProxies class is because the query uses lazy loading and it has not got the object yet.
After installing automapper (Install-Package AutoMapper);
Mapper.CreateMap<User, UserDto>();
UserDto dto = Mapper.DynamicMap<UserDto>(user);
Then return the dto.

MVC4 : Interface object in Model

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.

Code First Object not properly instantiating

I have a class department inheriting from activeentity
public class ActiveEntity : Entity, IActive
{
public ActiveEntity()
{
IsActive = true;
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
public bool IsActive { get; set; }
[Timestamp, ScaffoldColumn(false), DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Computed)]
public Byte[] Timestamp { get; set; }
[ScaffoldColumn(false)]
public string CreationUserId { get; set; }
[ScaffoldColumn(false)]
public string LastModifiedUserId { get; set; }
}
public class Department:ActiveEntity
{
public Department()
{
this.Address = new DepartmentAddress();
}
[StringLength(9),MinLength(9),MaxLength(9)]
public string Name { get; set; }
public Guid ManagerId { get; set; }
[UIHint("AjaxDropdown")]
public User Manager { get; set; }
public Guid? AddressId { get; set; }
public DepartmentAddress Address { get; set; }
public ICollection<OverheadRate> OverheadRates { get; set; }
}
I am just using annotations no Fluent API. The data saves to the data Sql Server 2008 just fine however the address object never gets instantiated, even though I have the context use the include
return c.Set<Department>().Include(d => d.Address).Include(d => d.Manager).Where(predicate);
The data is returned I run sql profiler and then run the query it returns the correct data.
Any thoughts or suggestions?
Remove instantiating the address (this.Address = new DepartmentAddress();) in the Department constructor. Instantiating navigation references in the default constructor is evil and has nasty side effects like these:
What would cause the Entity Framework to save an unloaded (but lazy loadable) reference over existing data?
EF 4.1 Code First: Why is EF not setting this navigation property?