EF Core 7 json columns , properties are not serialized - c#

I am working on a project using .Net 7 and EF Core 7.0.2.
I would like to use the new feature json columns but in the database is persisted wrong.
Here is my model:
[Serializable]
public class Customer
{
public long CustomerId { get; }
public string CustomerName { get; }
public CustomerDetail CustomerDetails { get; }
public DateTime CreatedAt { get; }
private Customer()
{
CustomerDetails = CustomerDetail.Empty;
CustomerName = string.Empty;
}
public Customer(long customerId, string customerName, CustomerDetail customerDetails, DateTime createdAt)
{
CustomerId = customerId;
CustomerName = customerName;
CustomerDetails = customerDetails;
CreatedAt = createdAt;
}
}
[Serializable]
public class CustomerDetail
{
private readonly List<Order> _orders;
public int LanguageId { get; }
public string CurrencySysname { get; }
public Card? PaymentMethod { get; }
public Address? Address { get; }
public IReadOnlyCollection<Order> Orders => _orders.ToArray();
private CustomerDetail()
{
CurrencySysname = string.Empty;
_orders = new List<Order>();
}
public CustomerDetail(IReadOnlyCollection<Order> orders, int languageId, string currencySysname, Card? paymentMethod, Address? address)
{
_orders = orders.ToList();
LanguageId = languageId;
CurrencySysname = currencySysname;
PaymentMethod = paymentMethod;
Address = address;
}
public static readonly CustomerDetail Empty = new CustomerDetail();
}
[Serializable]
public class Address
{
public int CityId { get; }
public int PostalCode { get; }
private Address()
{
}
public Address(int cityId, int postalCode)
{
CityId = cityId;
PostalCode = postalCode;
}
}
[Serializable]
public class Card
{
public int CardType { get; }
private Card()
{
}
public Card(int cardType)
{
CardType = cardType;
}
}
[Serializable]
public class Order
{
public int OrderId { get; }
public int Amount { get; }
private Order()
{
}
public Order(int orderId, int amount)
{
OrderId = orderId;
Amount = amount;
}
}
about the database configuration is :
class CustomerConfiguration : IEntityTypeConfiguration<Customer>
{
public const string Table = "Customers";
public void Configure(EntityTypeBuilder<Customer> builder)
{
builder.ToTable(Table);
builder.HasKey(x => x.CustomerId);
builder.Property(x => x.CustomerId).ValueGeneratedNever().IsRequired();
builder.Property(x => x.CustomerName).IsRequired();
builder.Property(x => x.CreatedAt).IsRequired();
builder.OwnsOne(x => x.CustomerDetails, details =>
{
details.ToJson();
details.OwnsOne(x => x.Address);
details.OwnsOne(x => x.PaymentMethod);
details.OwnsMany(x => x.Orders);
});
}
}
and when I generate the migration the result is that it semms correct :
public partial class Addcustomer : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Customers",
columns: table => new
{
CustomerId = table.Column<long>(type: "bigint", nullable: false),
CustomerName = table.Column<string>(type: "nvarchar(max)", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
CustomerDetails = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Customers", x => x.CustomerId);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Customers");
}
}
Finally when i am going to write in the database
[HttpGet("AddCustomer")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> AddCustomerAsync()
{
var orders = new List<Order>()
{
new Order(orderId: 1, amount: 4),
new Order(orderId: 2, amount: 5),
};
var customer = new Customer(
customerId: 123,
customerName: "Jim",
new CustomerDetail(
orders: orders,
languageId: 3,
currencySysname:"EUR",
paymentMethod: new Card(cardType: 1),
address: new Address(cityId: 3, postalCode: 123)),
createdAt: DateTime.Now);
using var db = new TestDbContext(_options);
db.Customers.Add(customer);
await db.SaveChangesAsync();
return Ok($"Pong - {DateTime.UtcNow:o}");
}
the result in the database is
https://prnt.sc/dyxyBF1oSp52
the objects address, orders payment method are empty.
{"Address":{},"Orders":[{},{}],"PaymentMethod":{}}
I try to understand what is going wrong, thanks in advance!
EF Core version: 7.0.2
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 7.0
Operating system: Windows 11
IDE: Visual Studio 2022 17.4.3

The problem is with your model, you are using get for all properties, but not set, so the corresponding property values will not be added to your database.
Please add set for each property of the model:
[Serializable]
public class Customer
{
public long CustomerId { get; set; }
public string CustomerName { get; set; }
public CustomerDetail CustomerDetails { get; set; }
public DateTime CreatedAt { get; set; }
public Customer()
{
CustomerDetails = CustomerDetail.Empty;
CustomerName = string.Empty;
}
public Customer(long customerId, string customerName, CustomerDetail customerDetails, DateTime createdAt)
{
CustomerId = customerId;
CustomerName = customerName;
CustomerDetails = customerDetails;
CreatedAt = createdAt;
}
}
[Serializable]
public class CustomerDetail
{
private readonly List<Order> _orders;
public int LanguageId { get; set; }
public string CurrencySysname { get; set; }
public Card? PaymentMethod { get; set; }
public Address? Address { get; set; }
public IReadOnlyCollection<Order> Orders => _orders.ToArray();
private CustomerDetail()
{
CurrencySysname = string.Empty;
_orders = new List<Order>();
}
public CustomerDetail(IReadOnlyCollection<Order> orders, int languageId, string currencySysname, Card? paymentMethod, Address? address)
{
_orders = orders.ToList();
LanguageId = languageId;
CurrencySysname = currencySysname;
PaymentMethod = paymentMethod;
Address = address;
}
public static readonly CustomerDetail Empty = new CustomerDetail();
}
[Serializable]
public class Address
{
public int CityId { get; set; }
public int PostalCode { get; set; }
private Address()
{
}
public Address(int cityId, int postalCode)
{
CityId = cityId;
PostalCode = postalCode;
}
}
[Serializable]
public class Card
{
public int CardType { get; set; }
private Card()
{
}
public Card(int cardType)
{
CardType = cardType;
}
}
[Serializable]
public class Order
{
public int OrderId { get; set; }
public int Amount { get; set; }
private Order()
{
}
public Order(int orderId, int amount)
{
OrderId = orderId;
Amount = amount;
}
}
Test Result:
Edit:
You didn't use set on the property, which means it only has read-only properties, so you can't set its value, so the field you save in database is empty.
You have to keep in mind that a property is just syntactic sugar for a pair of methods. One method (the getter) returns a value of the property type and one method (the setter) accepts a value of the property type.
So, there isn't really any way in general to "set" a property that doesn't have a setter.
Refer to Kyle's answer.

Also open a ticket in ef core repo and microsoft team answers in this question that :
Only read-write properties are mapped by convention. (But see #
4356.) Any property can be mapped by including it explicitly in the entity type mapping. For example:
details.Property(x => x.CurrencySysname);

Related

Can a related entity be refrenced in IEntityTypeConfiguration<T> configure method?

I have two model classes that are related:
Owner:
[Table("owner")]
public class Owner
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public ICollection<Account> Accounts { get; set; }
}
Account:
[Table("account")]
public class Account
{
public Guid Id { get; set; }
public DateTime DateCreated { get; set; }
public string AccountType { get; set; }
[ForeignKey(nameof(Owner))]
public Guid OwnerId { get; set; }
public Owner Owner { get; set; }
}
I'd like to be able to seed data using a configuration (IEntityTypeConfiguration<T>):
public class OwnerConfiguration : IEntityTypeConfiguration<Owner>
{
public void Configure(EntityTypeBuilder<Owner> builder)
{
builder.HasData
(
new Owner
{
Id = Guid.NewGuid(),
Name = "Owner A",
Address = "Address A"
}
);
}
}
public class AccountConfiguration : IEntityTypeConfiguration<Account>
{
public void Configure(EntityTypeBuilder<Account> builder)
{
builder.HasData
(
new Account
{
Id = Guid.NewGuid(),
DateCreated = DateTime.Now,
AccountType = "I"
// Owner = Owner A?
},
new Account
{
Id = Guid.NewGuid(),
DateCreated = DateTime.Now,
AccountType = "II"
// Owner = Owner A?
}
);
}
}
How do I assign an Owner to an Account in the Configure method?
If you are using Guid.NewGuid for the primary keys, then every time the seed method runs, it will create new objects. The safest way is to specify a fixed value for the PK which means you can use it across both account and owner. For example:
Create one or more constants for your entities:
private const string OwnerAId = "38059984-50ac-45bf-a96b-d07043780955";
private const string AccountAId = "e83d91a9-3022-4298-9c40-3c53c1fc0595";
Now use those constants where necessary:
public class OwnerConfiguration : IEntityTypeConfiguration<Owner>
{
public void Configure(EntityTypeBuilder<Owner> builder)
{
builder.HasData
(
new Owner
{
Id = Guid.Parse(OwnerAId), //<----------
Name = "Owner A",
Address = "Address A"
}
);
}
}
public class AccountConfiguration : IEntityTypeConfiguration<Account>
{
public void Configure(EntityTypeBuilder<Account> builder)
{
builder.HasData
(
new Account
{
Id = Guid.Parse(AccountAId), //<----------
DateCreated = DateTime.Now,
AccountType = "I"
OwnerId = Guid.Parse(OwnerAId) //<----------
},
//etc....

Linq query to object cannot pass Xunit test

I have strange problem. I have to pass the following Xunit test:
Fact]
public void GetOrder_ValidId_ReturnsCompleteOrder()
{
var service = new DataService();
var order = service.GetOrder(10248);
Assert.Equal(3, order.OrderDetails.Count);
Assert.Equal("Queso Cabrales", order.OrderDetails.First().Product.Name);
Assert.Equal("Dairy Products", order.OrderDetails.First().Product.Category.Name);
}
I am able to pass the orderdetails list to the test. However the "orderdetail" objects that i pass have a product object with null.
My classes are:
public class Order
{
[Key] public int Id { get; set; }
public DateTime Date { get; set; }
public DateTime Required { get; set; }
[Required] public virtual ICollection<OrderDetail> OrderDetails { get; set; }
public string ShipName { get; set; }
public string ShipCity { get; set; }
public Order()
{
this.OrderDetails = new List<OrderDetail>();
}
public override string ToString()
{
string ret =
$"Id = {Id}, DateTime = {Date}, Required = {Required}, shipName= {ShipName}, Shipcity = {ShipCity}";
return ret;
}
}
public class OrderDetail
{
public Order Order { get; set; }
[ForeignKey("orders")]
public int Orderid { get; set; }
[ForeignKey("products")]
public int productid { get; set; }
public int UnitPrice { get; set; }
public int Quantity { get; set; }
public int Discount { get; set; }
[Required] public virtual Product Product { get; set; }
public OrderDetail()
{
Product = Product;
}
public override string ToString()
{
return
$"OrderId = {Orderid}, Productid = {productid}";
}
}
public class Product
{
[ForeignKey("orderdetails")] public int Id { get; set; }
public string Name { get; set; }
public float UnitPrice { get; set; }
public string QuantityPerUnit { get; set; }
public int UnitsInStock { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get; set; }
public override string ToString()
{
return
$"Id = {Id}, Name = {Name}, UnitPrice = {UnitPrice}, QuantityPerUnit = {QuantityPerUnit}, UnitsInStock = {UnitsInStock}, CategoryId = {CategoryId}";
}
}
I have the tried the following two solution:
public Order GetOrder(int id)
{
using var ctx = new NorthWindContext();
var query = ctx.Orders.AsQueryable().Where(o => o.Id == 10248).FirstOrDefault();
ctx.SaveChanges();
//var query2 = ctx.Orders.Include("orderdetails").Where()
return query;
}
and
var query2 = ctx.Orders.Where(o => o.Id == 10248)
.Select(a => new Order
{
Id = a.Id,
OrderDetails = a.OrderDetails
}).FirstOrDefault();
I have tried to reconfigure the mapping but didn't do it.
If i do the same query and use Console.Writeline in a foreach loop i can conclude that every orderdetail has a "product"...
I keep on the getting the error: "Object not set to an instance of an object" when passing to xUnit test.
Ivans response did the job.
First of all i removed using System.Data.Entity;
Then i changed the order class to the following:
public class Order
{
[Key] public int Id { get; set; }
public DateTime Date { get; set; }
public DateTime Required { get; set; }
[Required] public virtual List<OrderDetail> OrderDetails { get; set; }
public string ShipName { get; set; }
public string ShipCity { get; set; }
public Order()
{
this.OrderDetails = new List<OrderDetail>();
}
public override string ToString()
{
string ret =
$"Id = {Id}, DateTime = {Date}, Required = {Required}, shipName= {ShipName}, Shipcity = {ShipCity}";
return ret;
}
}
Then i used the following query just like Ivans:
var query3 = ctx.Orders
.Include(o => o.OrderDetails)
.ThenInclude(d => d.Product)
.ThenInclude(d => d.Category)
.AsSingleQuery()
.FirstOrDefault(o => o.Id == 10248);

Automapper based on dynamic conditions

I have multiple classes
I am using automapper to map the classes
class Country
{
public int Countryid {get;set}
public string CountryEnglishName {get;set;}
public string CountryArabicName {get;set;}
public Location Location{get;set;}
}
class Location
{
public int Locationid {get;set}
public string LocationEnglishName {get;set;}
public string LocationArabicName {get;set;}
}
class Customer
{
public int Customerid {get;set}
public string CustomerName {get;set;}
public int LocationId
public Location Location{get;set;}
public string PropertyArabicName {get;set;}
public string PropertyEnglishName{get;set;}
}
And my DTO looks like below
class CustomerDetailsReadDTO
{
public int Customerid {get;set}
public string PropertyName {get;set;}
public string CustomerName {get;set;}
public string CountryName {get;set;}
public string LocationName {get;set;}
}
If user pass API parameter as "en-US" then CountryDTO class field Country should contain with CountryEnglishName like that Location field also.
My mapping looks like below
class AutomapperProfile :Profile
{
CreateMap<Customer,CustomerDetailsReadDTO>
.ForMember(dest=>dest.CountryName,opt.MapFrom<CustomResolver,string>(src=>src.Location.Country.CountryEnglishName))
.ForMember(dest=>dest.LocationName,opt.MapFrom<CustomResolver,string>(src=>src.Location.LocationEnglishName))
.ForMember(dest=>dest.PropertyName,opt.MapFrom<CustomResolver,string>(src=>src.Location.PropertyNameEnglishName));
}
my interface and customresolver looks like below
This interface has been scoped in startup class
public interface ILanguage
{
string Language{get;set;}
}
public class CustomResolver:IMemberResolver<object,object,string,string>
{
private string _Lang;
public CustomResolver(ILanguage ilanguage)
{
_Lang = ilanguage;
}
public string Resolve(object source,object destination,string sourcemember,string destmember,ResoutionContext context)
{
var type = source.GetType();
switch(Type.Name)
{
case "Country":
var country =(Country)source;
return _Lang == "en-US" ? country.CountryEnglishName :country.CountryArabicName;
case "Location":
var location =(Location)source;
return _Lang == "en-US" ? location.locationEnglishName :location.locationArabicName;
}
}
}
Iam getting correctvalues in PropertyName after mapping.While passing ar-SA iam getting arabicpropertyname else englishpropertyname.
Here the propblem is after mapping iam getting CountryName and LocationName value as empty string .
Can we get correct values for CountryName and LocationName based on language?
Can we solve this ?
Here is a fully working sample console project, that does what you want:
using System;
using System.Diagnostics;
using AutoMapper;
using Microsoft.Extensions.DependencyInjection;
namespace IssueConsoleTemplate
{
class Country
{
public int Countryid { get; set; }
public string CountryEnglishName { get; set; }
public string CountryArabicName { get; set; }
}
class Location
{
public int Locationid { get; set; }
public string LocationEnglishName { get; set; }
public string LocationArabicName { get; set; }
public Country Country { get; set; }
}
class Customer
{
public int Customerid { get; set; }
public string CustomerName { get; set; }
public int LocationId { get; set; }
public Location Location { get; set; }
public string PropertyArabicName { get; set; }
public string PropertyEnglishName { get; set; }
}
class CustomerDetailsReadDTO
{
public int Customerid { get; set; }
public string PropertyName { get; set; }
public string CustomerName { get; set; }
public string CountryName { get; set; }
public string LocationName { get; set; }
}
class AutomapperProfile : Profile
{
public AutomapperProfile(IServiceProvider serviceProvider)
{
CreateMap<Customer, CustomerDetailsReadDTO>()
.ForMember(
dest => dest.CountryName,
opt => opt.MapFrom(
s => serviceProvider.GetService<ILanguage>().Language == "en-US"
? s.Location.Country.CountryEnglishName
: s.Location.Country.CountryArabicName))
.ForMember(
dest => dest.LocationName,
opt => opt.MapFrom(
s => serviceProvider.GetService<ILanguage>().Language == "en-US"
? s.Location.LocationEnglishName
: s.Location.LocationArabicName))
.ForMember(
dest => dest.PropertyName,
opt => opt.MapFrom(
s => serviceProvider.GetService<ILanguage>().Language == "en-US"
? s.PropertyEnglishName
: s.PropertyArabicName));
}
}
interface ILanguage
{
string Language { get; set; }
}
class CurrentLanguage : ILanguage
{
public string Language { get; set; }
}
internal static class Program
{
private static void Main(string[] args)
{
var serviceProvider = new ServiceCollection()
.AddScoped<ILanguage>(p => new CurrentLanguage())
.BuildServiceProvider();
var config = new MapperConfiguration(
cfg =>
{
cfg.ConstructServicesUsing(t => serviceProvider.GetService(t));
cfg.AddProfile(new AutomapperProfile(serviceProvider));
});
var mapper = config.CreateMapper();
var customer = new Customer
{
Customerid = 1,
CustomerName = "John",
LocationId = 1,
Location = new Location
{
Locationid = 1,
LocationEnglishName = "New York",
LocationArabicName = "نِيويورْك",
Country = new Country
{
Countryid = 1,
CountryEnglishName = "USA",
CountryArabicName = "الوِلايات المُتَّحِدة الأَمْريكيّة",
}
},
PropertyArabicName = "مبني المقاطعة الملكية",
PropertyEnglishName = "Empire State Building",
};
serviceProvider.GetService<ILanguage>().Language = "en-US";
var englishDto = mapper.Map<CustomerDetailsReadDTO>(customer);
serviceProvider.GetService<ILanguage>().Language = "ar-SA";
var arabicDto = mapper.Map<CustomerDetailsReadDTO>(customer);
Debug.Assert(englishDto.CountryName == "USA");
Debug.Assert(englishDto.LocationName == "New York");
Debug.Assert(englishDto.PropertyName == "Empire State Building");
Debug.Assert(arabicDto.CountryName == "الوِلايات المُتَّحِدة الأَمْريكيّة");
Debug.Assert(arabicDto.LocationName == "نِيويورْك");
Debug.Assert(arabicDto.PropertyName == "مبني المقاطعة الملكية");
}
}
}
I added a Country property to the Location class, to make sense of the model.
Though you could use IMemberResolver, you don't really need to (as shown in the sample code).

EF Core database configuration

Hi I'm needing a little help with EF CORE in WPF I'm still new to ef core, I'm getting duplicate entry with primary key when adding a job, I'm assuming its trying to insert the same contact and department into the tables where I'm currently storing them.
Error when adding a job is
Microsoft.EntityFrameworkCore.DbUpdateException: 'An error occurred while updating the entries. See the inner exception for details.'
Inner exception
MySqlException: Duplicate entry '4' for key 'PRIMARY'
all values passed to the add job command have the correct values
code is
public static class JobsService
{
public static async Task<List<Job>> GetJobs()
{
using (var db = new DatabaseContext())
{
db.Database.EnsureCreated();
return await db.Jobs.ToListAsync();
}
}
public static async Task AddJob(string title, string partNumber, double quantity, Contact assignedTo, Department department)
{
using(var db = new DatabaseContext())
{
db.Database.EnsureCreated();
var job = new Job(title, partNumber, quantity, assignedTo, department);
await db.Jobs.AddAsync(job);
await db.SaveChangesAsync();
}
}
}
public class Job
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public double Quantity { get; set; }
public double? ReceivedQuantity { get; set; }
public string PartNumber { get; set; }
public DateTimeOffset? CreatedDateTime { get; set; }
public User CreatedBy { get; set; }
public Contact AssignedTo { get; set; }
public Contact? CompletedBy { get; set; }
public DateTimeOffset? CompletedDateTime { get; set; }
public Department Department { get; set; }
public JobStatus Status { get; set; }
public Job(string title, string partNumber, double quantity, Contact assignedTo, Department department)
{
Title = title;
PartNumber = partNumber;
Quantity = quantity;
AssignedTo = assignedTo;
Department = department;
CreatedBy = UsersService.LoggedInUser;
CreatedDateTime = DateTimeOffset.Now;
}
public Job()
{
}
}
public class Contact
{
[Key]
public int Id { get; set; }
public ContactType Type { get; set; }
public Title Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string TelephoneNumber { get; set; }
public bool IsDeleted { get; set; }
public DateTimeOffset? DeletedDateTime { get; internal set; }
public string FullName { get; private set; }
public Address? Address { get; set; }
public Contact(ContactType type, Title title, string firstName, string lastName, string emailAddress, string telephoneNumber)
{
Type = type;
Title = Title;
FirstName = firstName;
LastName = lastName;
EmailAddress = emailAddress;
TelephoneNumber = telephoneNumber;
IsDeleted = false;
Address = new Address();
}
public Contact()
{
}
public void AddAddress(string line1, string line2, string line3, string city, string county, string postCode, Country country)
{
Address = new Address(line1, line2, line3, city, county, postCode, country);
}
public void Update(ContactType type, Title title, string firstName, string lastName, string emailAddress, string telephoneNumber)
{
Type = type;
Title = title;
FirstName = firstName;
LastName = lastName;
EmailAddress = emailAddress;
TelephoneNumber = telephoneNumber;
}
public void Update(Contact contact)
{
Type = contact.Type;
Title = contact.Title;
FirstName = contact.FirstName;
LastName = contact.LastName;
EmailAddress = contact.EmailAddress;
TelephoneNumber = contact.TelephoneNumber;
Address = contact.Address;
}
public void Delete()
{
if(IsDeleted)
{
return;
}
IsDeleted = true;
DeletedDateTime = DateTimeOffset.UtcNow;
}
public void Restore()
{
IsDeleted = false;
DeletedDateTime = null;
}
}
public class ContactEntityTypeConfiguration : IEntityTypeConfiguration<Contact>
{
public void Configure(EntityTypeBuilder<Contact> builder)
{
builder.Property(x => x.FullName)
.HasComputedColumnSql($"CONCAT(FirstName, ' ', LastName)");
}
}
public class Department
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public Department()
{
}
public Department(string name)
{
Name = name;
}
}
public class DepartmentEntityTypeConfiguration : IEntityTypeConfiguration<Department>
{
public void Configure(EntityTypeBuilder<Department> builder)
{
builder.HasKey(x => x.Id);
}
}
class DatabaseContext : DbContext
{
public DbSet<Item> Inventory { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Tags> ItemTags { get; set; }
public DbSet<TaggedItems> TaggedItems { get; set; }
public DbSet<Contact> Contacts { get; set; }
public DbSet<SavedDirectory> Directorys { get; set; }
public DbSet<Job> Jobs { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Country> Countries { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var settings = ConfigurationManager.ConnectionStrings;
if (settings != null)
{
foreach (ConnectionStringSettings cs in settings)
{
optionsBuilder.UseMySQL(cs.ConnectionString);
}
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Item>(entity =>
{
entity.HasKey(e => e.PartNumber);
});
modelBuilder.Entity<Permission>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.Entity<CloudAccount>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.Entity<Tags>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.Entity<User>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasMany(e => e.CloudAccounts);
});
modelBuilder.Entity<Contact>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.Entity<SavedDirectory>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.Entity<Job>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.Entity<Country>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.ApplyConfiguration(new ContactEntityTypeConfiguration());
modelBuilder.ApplyConfiguration(new DepartmentEntityTypeConfiguration());
}
}
This occurs due to update in the database model please goto edmx file and import your database model again to it. this will help you. but if still you facing the same issue then Truncate your previous data in database and import the model again to configure the injectors. then run hope that will fix your error.

An exception of type 'System.Data.Entity.Infrastructure.DbUpdateException' occurred

I run into this error when I try to insert entries to my database. I can seed data manually but when I invoke the POST method in the Web API I get this error.
here's my user entity class:
namespace YourTime.DataAccess.UserEntities
{
[Table("Users")]
public class UserProfile
{
[Key]
public int UserId { get; set; }
public string Activities { get; set; }
public DateTime Birthday { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FavoriteMusic { get; set; }
public bool Verified { get; set; }
public string Gender { get; set; }
public string Email { get; set; }
public DateTime UpdatedTime { get; set; }
public string Hometown { get; set; }
public virtual CoverPhoto Cover { get; set; }
public virtual ICollection<Status> Statuses { get; set; }
public virtual ICollection<Event> Events { get; set; }
}
}
I'm using data access layer as an intermediate layer to fetch and update data. I have Repository class that is laid out like this:
namespace YourTime.DataAccess.Repositories
{
public abstract class Repository<T>: IRespository<T> where T : class
{
private readonly YourTimeProjectContext _context;
protected Repository(YourTimeProjectContext context)
{
_context = context;
}
public virtual List<T> Get()
{
return _context.Set<T>().ToList();
}
public virtual T Get(int id)
{
return _context.Set<T>().Find(id);
}
public virtual T Update(T obj)
{
_context.Entry(obj).State = EntityState.Modified;
_context.SaveChanges();
return obj;
}
public virtual T Insert(T obj)
{
_context.Set<T>().Add(obj);
_context.SaveChanges();
return obj;
}
public virtual int Delete(T obj)
{
_context.Set<T>().Remove(obj);
_context.SaveChanges();
return _context.SaveChanges();
}
internal object Insert(object UserEntity)
{
throw new System.NotImplementedException();
}
}
}
Here's my model class
namespace YourTime.Models
{
public class UserModel
{
public int UserId { get; set; }
public DateTime Birthday { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Verified { get; set; }
public string Gender { get; set; }
public string Email { get; set; }
public DateTime UpdatedTime { get; set; }
public int StatusCount { get; set; }
}
The Model factory class converts the entities to models back and forth. here's the code for the ModelFactory class
namespace YourTime.DataAccess.Repositories
{
public interface IModelFactory
{
UserModel Create(UserProfile user);
UserProfile Create(UserModel userModel);
}
public class ModelFactory : IModelFactory
{
public UserModel Create(UserProfile user)
{
return new UserModel
{
FirstName = user.FirstName,
Birthday = user.Birthday!=null? user.Birthday:DateTime.Now,
Email = user.Email,
Gender = user.Gender,
UserId = user.UserId,
LastName = user.LastName,
StatusCount = user.Statuses!=null? user.Statuses.Count:0,
UpdatedTime = user.UpdatedTime!=null? user.UpdatedTime:DateTime.Today,
Verified = user.Verified!=null? user.Verified:true
};
}
public UserProfile Create(UserModel user)
{
return new UserProfile
{
UserId = user.UserId,
FirstName = user.FirstName,
Email = user.Email,
Gender = user.Gender,
LastName = user.LastName
};
}
}
}
and finally here's my controller class for the API:
namespace YourTime.Controllers
{
public class UserController : ApiController{
private IWebService _service;
private IModelFactory _modelFactory;
public UserController( ) {
_service = new WebService( );
_modelFactory = new ModelFactory( );
}
public IHttpActionResult Get( ) {
var users = _service.Users.Get( );
var models = users.Select( _modelFactory.Create );
return Ok( models );
}
public IHttpActionResult Get( int id ) {
try {
var user = _service.Users.Get( id );
var model = _modelFactory.Create( user );
return Ok( model );
} catch ( Exception ex ) {
//Logging
#if DEBUG
return InternalServerError( ex );
#endif
return InternalServerError( );
}
}
public IHttpActionResult Post([FromBody]UserModel userModel ) {
var userEntity = _modelFactory.Create( userModel );
var user = _service.Users.Insert( userEntity );
var model = _modelFactory.Create( user );
return Created(string.Format("http://localhost:52962/api/user/{0}", model.UserId), model);
}
}
}
I encounter the error at '_context.SaveChanges()'. I'm passing in 3 parameters from the body, FirstName, LastName and UserId. Rest are set to default values. Any idea what may the problem?
Edit:
My seed code looks like this:
var status = new Status { Message = "Hello. This is a test", UpdateTime = DateTime.Now };
var user1 = new UserProfile { FirstName = "Federica", LastName = "Fenu", Birthday = DateTime.Parse("9/10/1991"), UpdatedTime = DateTime.Now, Gender = "Male", Email = "gmujtaba_2005#hotmail.com", Statuses = new List<Status> { status } };
var user2 = new UserProfile { FirstName = "Joe", LastName = "Goddard", Birthday = DateTime.Parse("9/10/1991"), UpdatedTime = DateTime.Now, Gender = "Male", Email = "gmujtaba_2005#yahoo.com", Statuses = new List<Status> { status } };
context.UserProfiles.Add(user1);
context.UserProfiles.Add(user2);
and here's the webservice class:
namespace YourTime.DataAccess.Repositories
{
public class WebService: IWebService
{
private Repository<UserProfile> _users;
private Repository<Status> _statuses;
private Repository<Video> _videos;
private Repository<Event> _events;
private Repository<CoverPhoto> _coverphotos;
public Repository<UserProfile> Users
{
get
{
if (_users == null)
_users = new UserRepository(new YourTimeProjectContext());
return _users;
}
}
public Repository<Status> Statuses
{
get
{
if(_statuses==null)
_statuses = new StatusRepository(new YourTimeProjectContext());
return _statuses;
}
}
public Repository<Video> Videos
{
get
{
if(_videos==null)
_videos = new VideoRepository(new YourTimeProjectContext());
return _videos;
}
}
public Repository<Event> Events
{
get
{
if(_events==null)
_events = new EventRepository(new YourTimeProjectContext());
return _events;
}
}
public Repository<CoverPhoto> CoverPhotos
{
get
{
if(_coverphotos == null)
_coverphotos = new CoverPhotoRespository(new YourTimeProjectContext());
return _coverphotos;
}
}
}
}
I found the problem in my code. The parameters I was passing from the body didn't match the parameters required in the Create function of the User. It required the following values:
UserId = user.UserId,
FirstName = user.FirstName,
Email = user.Email,
Gender = user.Gender,
LastName = user.LastName
I was only passing FirstName, LastName and UserId. This is why the changes weren't being saved in the dbContext.

Categories