Entity Framework Core PostgreSQL json query - postgresql

Always return empty results
var collection = await _context.Settings
.Select(s => new
{
s.SettingId,
s.SettingParentId,
SettingValue = s.SettingValue.GetProperty(lang)
})
.Where(s => EF.Functions.JsonExists(s.SettingValue, lang))
.ToListAsync();
I try return only key from json but always return empty, when remove "select" works fine but i need only one key
This is the model
public class Setting
{
public string SettingId { get; set; }
public string SettingParentId { get; set; }
public JsonElement SettingValue { get; set; }
}

I think you're looking for something like the following:
class Program
{
static async Task Main(string[] args)
{
await using var ctx = new BlogContext();
await ctx.Database.EnsureDeletedAsync();
await ctx.Database.EnsureCreatedAsync();
var lang = "es";
var collection = await ctx.Settings
.Where(s => EF.Functions.JsonExists(s.SettingValue, lang))
.Select(s => new
{
s.SettingId,
s.SettingParentId,
SettingValue = s.SettingValue.GetProperty(lang).GetString()
})
.ToListAsync();
foreach (var i in collection)
{
Console.WriteLine($"{i.SettingId}: {i.SettingValue}");
}
}
}
public class BlogContext : DbContext
{
public DbSet<Setting> Settings { get; set; }
static ILoggerFactory ContextLoggerFactory
=> LoggerFactory.Create(b => b.AddConsole().AddFilter("", LogLevel.Information));
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseNpgsql(#"Host=localhost;Username=test;Password=test")
.EnableSensitiveDataLogging()
.UseLoggerFactory(ContextLoggerFactory);
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Setting>().HasData(new Setting
{
SettingId = "1",
SettingValue = JsonDocument.Parse(#"{ ""es"" : ""valor""}").RootElement
});
}
}
public class Setting
{
public string SettingId { get; set; }
public string SettingParentId { get; set; }
public JsonElement SettingValue { get; set; }
}

Related

How to insert payload data in many-to-many relationships with EF Core 5

I have this relationship between Licitadores and Ofertas
public class Licitador
{
public int Id { get; set; }
public string Nombre { get; set; }
[StringLength(maximumLength: 15)]
public string CodigoSAP { get; set; }
public List<Oferta> Ofertas { get; set; } = new List<Oferta>();
}
public class Oferta
{
[StringLength(maximumLength:6)]
public string Id { get; set; }
[StringLength(maximumLength: 5)]
public string IdPresentada { get; set; }
....
public List<Licitador> Licitadores { get; set; } = new List<Licitador>();
}
And the join table in the context
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<LicitacionesEnSolitario>().ToTable("LicitacionesSolitario");
modelBuilder.Entity<Licitador>()
.HasMany(o => o.Ofertas)
.WithMany(of => of.Licitadores)
.UsingEntity<LicitacionesEnSolitario>
(oo => oo.HasOne<Oferta>().WithMany(),
oo => oo.HasOne<Licitador>().WithMany())
.Property(oo => oo.Adjudicado)
.IsRequired();
}
I need this data in my entity/table LicitacionesEnSolitario in addition to PK y FK
public class LicitacionesEnSolitario
{
public int LicitadorId { get; set; }
public string OfertaId { get; set; }
public bool Adjudicado { get; set; }
public string Plazo { get; set; }
public decimal PresupuestoOfertado { get; set; }
public DateTime? FechaAdjudicacion { get; set; }
}
Here I insert the data importing them from another database
public int ImportarLicitacionesEnSolitario()
{
try
{
int registrosAñadidos = 0;
var registrosSAP = _contextSAP.LicitacionesEnSolitario
.FromSqlRaw("sql")
.ToList();
foreach (var registroSAP in registrosSAP)
{
var oferta = _contextBoletus.Ofertas.Find(registroSAP.OfertaId);
var licitador = _contextBoletus.Licitadores.Where(l => l.CodigoSAP == registroSAP.CodigoSAP).FirstOrDefault();
oferta.Licitadores.Add(licitador);
registrosAñadidos +=1;
}
_contextBoletus.SaveChanges();
return registrosAñadidos;
}
catch (Exception ex)
{
throw ex;
}
}
This works fine and insert data in "LicitacionesEnSolitario" but with this fields Adjudicado, Plazo, PresupuestoPfertado y FechaAdjudicacion with nulls.
I don't know how to insert them at the time I insert Licitadores and if I try to update after the Add method using the PKs I just added
foreach (var registroSAP in registrosSAP)
{
var oferta = _contextBoletus.Ofertas.Find(registroSAP.OfertaId);
var licitador = _contextBoletus.Licitadores.Where(l => l.CodigoSAP == registroSAP.CodigoSAP).FirstOrDefault();
oferta.Licitadores.Add(licitador);
var ls = _contextBoletus.Set<LicitacionesEnSolitario>()
.SingleOrDefault(ls => ls.OfertaId == oferta.Id & ls.LicitadorId == licitador.Id);
ls.Adjudicado = registroSAP.Adjudicado;
ls.PresupuestoOfertado = registroSAP.PresupuestoOfertado;
ls.FechaAdjudicacion = registroSAP.FechaAdjudicacion;
registrosAñadidos +=1;
}
_contextBoletus.SaveChanges();
return registrosAñadidos;
I get this error System.NullReferenceException: Object reference not set to an instance of an object.
Any idea, please?
Thanks
This is the best way I found
foreach (var registroSAP in registrosSAP)
{
var oferta = _contextBoletus.Ofertas.Find(registroSAP.OfertaId);
var licitador = _contextBoletus.Licitadores.Where(l => l.CodigoSAP == registroSAP.CodigoSAP).FirstOrDefault();
var ls = _contextBoletus.Set<LicitacionesEnSolitario>().Add(
new LicitacionesEnSolitario
{
LicitadorId = licitador.Id,
OfertaId = oferta.Id,
Adjudicado = registroSAP.Adjudicado,
Plazo = registroSAP.Plazo,
PresupuestoOfertado = registroSAP.PresupuestoOfertado,
FechaAdjudicacion = registroSAP.FechaAdjudicacion
});
registrosAñadidos += 1;
}
Thanks

not catch DbUpdateConcurrencyException?

i set a break point at var c = context.SaveChanges() , then update sql update [Student] set Name ='456' where PriKey =1 in Sqlserver Management Studio, continue, somstimes the program can not catch DbUpdateConcurrencyException, why happen this situation?
public class OfficeContext : DbContext
{
public DbSet<Student> Students { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
#"Data Source=192.168.31.215;User ID=hj;Password=hj123;Database=office;Integrated Security=false");
optionsBuilder.LogTo(Console.WriteLine);
}
[Table("Student")]
public class Student
{
[Column("PriKey")]
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Timestamp]
[Column("VerCol")]
public virtual byte[] RowVersion { get; set; }
[Column("Name")]
public string Name { get; set; }
}
}
class Program
{
static void Main(string[] args)
{
using (var context = new OfficeContext()){
try
{
var a = context.Students.FirstOrDefault(x => x.Id == 1);
a.Name = "123";
var c = context.SaveChanges();
}
catch(DbUpdateConcurrencyException e)
{
Console.WriteLine(e.Message);
}
}
}
}

Can not set up one to many relationship with EF fluent API

I am trying to configure a one to many relationship using EF Core via fluent api and i keep getting the following error :
The expression 'x => x.parent' is not a valid property expression. The
expression should represent a simple property access: 't =>
t.MyProperty'. (Parameter 'propertyAccessExpression')'
Model(s)
public class Parent {
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child {
public int ID { get; set; }
public string Name { get; set; }
public Parent parent;
public int ParentId { get; set; }
}
Context
public class MyContext : DbContext {
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Child>().HasKey(x => x.ID);
modelBuilder.Entity<Parent>().HasKey(x => x.ID);
modelBuilder.Entity<Child>()
.HasOne(x => x.parent)
.WithMany(y => y.Children)
.HasForeignKey(t => t.ParentId);
}
public MyContext(DbContextOptions options):base(options) { }
}
Usage
static async Task Main(string[] args)
{
string connectionString = "[someconnectionstring]"
var optionsBuilder = new DbContextOptionsBuilder<MyContext>();
optionsBuilder.UseSqlServer(connectionString);
MyContext context = new MyContext(optionsBuilder.Options);
await context.Parents.AddAsync(new Parent {
Name = "myparent",
Children = new List<Child>() {
new Child { Name = "Child1" },
new Child { Name = "Child2" } }
}); //i am getting the error here
await context.SaveChangesAsync();
}
parent in Child class is a field. It should be public property. Please see for more information https://learn.microsoft.com/en-us/ef/ef6/modeling/code-first/fluent/types-and-properties#property-mapping

ASP.Net Core EF M-M Instance Cannot be Tracked

Consider the following error:
InvalidOperationException: The instance of entity type 'OrderRegion' cannot be tracked because another instance with the key value '[Orderid: 10, RegionId: 1]' is already being tracked...
Also, consider the following classes (slightly snipped for brevity):
public class Order
…
[Key]
public int Id { get; set; }
…
[Display(Name = "Regions")]
public ICollection<OrderRegion> OrderRegions { get; set; }
[Display(Name = "Stores")]
public ICollection<OrderStore> OrderStores { get; set; }
public class OrderRegion
{
//[Key]
public int OrderId { get; set; }
public Order Order { get; set; }
//[Key]
public int RegionId { get; set; }
public Region Region { get; set; }
}
public class OrderStore
{
//[Key]
public int OrderId { get; set; }
public Order Order { get; set; }
//[Key]
public int StoreId { get; set; }
public Store Store { get; set; }
}
Also of relevance is the Context where I create the relationships:
public class MyContext:DbContext
{
public MyContext(DbContextOptions<AzureOrdersContext> options) : base(options) { }
public DbSet<Order> Order { get; set; }
public DbSet<OrderRegion> OrderRegion { get; set; }
public DbSet<OrderStore> OrderStore { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
…
modelBuilder.Entity<OrderRegion>()
.HasKey(nr => new { nr.OrderId, nr.RegionId });
modelBuilder.Entity<OrderRegion>()
.HasOne(nr => nr.Order)
.WithMany(n => n.OrderRegions)
.HasForeignKey(nr => nr.OrderId);
modelBuilder.Entity<OrderRegion>()
.HasOne(nr => nr.Region)
.WithMany(n => n.OrderRegions)
.HasForeignKey(nr => nr.RegionId);
modelBuilder.Entity<OrderStore>()
.HasKey(nr => new { nr.OrderId, nr.StoreId });
modelBuilder.Entity<OrderStore>()
.HasOne(nr => nr.Order)
.WithMany(n => n.OrderStores)
.HasForeignKey(nr => nr.OrderId);
modelBuilder.Entity<OrderStore>()
.HasOne(nr => nr.Store)
.WithMany(n => n.OrderStores)
.HasForeignKey(nr => nr.StoreId);
}
}
And finally, my "Edit.cshtml.cs" where my error is thrown (snipped a lot):
public async Task<IActionResult> OnPostAsync(int? id, int[] AssignedRegions, int[] AssignedStores)
{
if (!ModelState.IsValid)
{
return Page();
}
var Ordertoupdate = await _context.Order
.Include(i => i.OrderRegions).ThenInclude(navigationPropertyPath: i => i.Region)
.Include(i => i.OrderStores).ThenInclude(navigationPropertyPath: i => i.Store)
.FirstOrDefaultAsync(m => m.Id == id);
...
if (await TryUpdateModelAsync<Web.Models.Order>(
Ordertoupdate,
"Order",
i => i.CreatedOn,
i => i.CreatedBy,
i => i.ModifiedBy, i => i.ExpirationDate,
...))
{
UpdateOrderRegions(_context, AssignedRegions, Ordertoupdate);
UpdateOrderStores(_context, AssignedStores, Ordertoupdate);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
UpdateOrderRegions(_context, AssignedRegions, Ordertoupdate);
UpdateOrderStores(_context, AssignedStores, Ordertoupdate);
PopulateAssignedRegions(_context, Ordertoupdate);
PopulateAssignedStores(_context, Ordertoupdate);
return Page();
}
The error is getting thrown on _context.SaveChangesAsync(); Any ideas? I'm certain I'm just doing something stupid and not seeing a simple fix.
Updating to include UpdateOrderRegions as requested:
public void UpdateOrderRegions (AzureOrdersContext _context, int[] SelectedRegions, Web.Models.Order OrderToUpdate)
{
if (SelectedRegions == null)
{
OrderToUpdate.OrderRegions = new List<OrderRegion>();
return;
}
var StoreRegionsToDelete= OrderToUpdate.OrderRegions.Where<OrderRegion>(nr => {
return !SelectedRegions.AsQueryable<Int32>().Contains<Int32>(nr.RegionId);
});
StoreRegionsToDelete.ToList().ForEach(r => { OrderToUpdate.OrderRegions.Remove(r); });
var StoreRegionsToAdd = SelectedRegions.AsQueryable<Int32>().Where<Int32>(regionId =>
!OrderToUpdate.OrderRegions.Any( nr=> nr.RegionId == regionId)
);
StoreRegionsToAdd.ToList().ForEach(regionId =>
OrderToUpdate.OrderRegions.Add(new OrderRegion
{
OrderId = OrderToUpdate.Id,
RegionId = regionId
}));
////This is where a different, more frustrating logical error lives but isn't related to my EF error
////Attempting to model after: https://github.com/aspnet/Docs/blob/master/aspnetcore/data/ef-rp/intro/samples/cu/Pages/Instructors/InstructorCoursesPageModel.cshtml.cs
var selectedRegionHS = new HashSet<string>(SelectedRegions);
var regionOrders = new HashSet<int>(OrderToUpdate.OrderRegions.Select(c => c.Order.Id));
foreach (var thisregion in _context.Region)
{
if (selectedRegionHS.Contains(thisregion.Id.ToString()))
{
if (!regionOrders.Contains(thisregion.Id))
{
OrderToUpdate.OrderRegions.Add(
new OrderRegion
{
OrderId = OrderToUpdate.Id,
RegionId = thisregion.Id
});
}
}
else
{
if (regionOrders.Contains(thisregion.Id))
{
OrderRegion RegionToRemove = OrderToUpdate.OrderRegions.SingleOrDefault(i => i.RegionId == thisregion.Id);
_context.Remove(RegionToRemove);
}
}
}
}
This issue occurs when the context already has an item tracked and you explicitly try attaching a new object with the same key.
Considering the error is on OrderRegion and this type has a composite key of OrderId/RegionId, I think it is likely that you are retrieving the OrderRegion and attaching a new OrderRegion with the same OrderId/RegionId combination. You may need to check if an OrderRegion key already exists or clear the Order's regions and rebuild the list to avoid this collision.
I hope this points you in the right direction. Feel free to provide the code where you handle the OrderRegion updates and I'll try to further assist.

EF Code First mapping for collection

I'm using EF 4.1 RC Code first. I have a many to many relation working with a composite PK in the junction table Friends. We explicitly need a separate Friends class (don't ask) which represents our junction table. Our goal is to be able to control the delete process from the User entity. Please read this before reading the rest: http://mocella.blogspot.com/2010/01/entity-framework-v4-object-graph.html. So, we managed to create our composite PK but this broke our mapping for the collection. The question is how to map FriendsCol?
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public virtual ICollecion<Friends> FriendsCol { get; set; }
}
public class Friends
{
public int User1Id { get; set; }
public int User2Id { get; set; }
public User User1 { get; set; }
public User User2 { get; set; }
}
Have a composite key mapping
public class FriendsMap : EntityTypeConfiguration<Friends>
{
HasKey(m => new { m.userId1 , m.userId2 });
//this.HasRequired(x => x.User1)
//.WithMany()
//.HasForeignKey(x => x.User1Id)
//.WillCascadeOnDelete(false);
//this.HasRequired(x => x.User2)
// .WithMany()
// .HasForeignKey(x => x.User2Id)
// .WillCascadeOnDelete(false);
}
public class UserMap : EntityTypeConfiguration<UserNew>
{
public UserMap()
{
ToTable("users");
Property(user => user.Name).HasColumnName("name");
// HasMany<Friends>(user => user.FriendsCol).WithMany();
}
}
What about this:
public class FriendsMap : EntityTypeConfiguration<Friends>
{
HasKey(m => new { m.userId1 , m.userId2 });
this.HasRequired(x => x.User1)
.WithMany()
.HasForeignKey(x => x.User1Id)
.WillCascadeOnDelete(false);
this.HasRequired(x => x.User2)
.WithMany(u => u.FriendsCol)
.HasForeignKey(x => x.User2Id)
.WillCascadeOnDelete(false);
}
public class UserMap : EntityTypeConfiguration<UserNew>
{
public UserMap()
{
ToTable("users");
Property(user => user.Name).HasColumnName("name");
}
}
Edit:
I just made very simple example and it works without any problem:
class Program
{
static void Main(string[] args)
{
using (var context = new Context())
{
context.Database.Delete();
context.Database.CreateIfNotExists();
var u1 = new User() { Name = "A" };
var u2 = new User() { Name = "B" };
var u3 = new User() { Name = "C" };
var f1 = new Friends() { User1 = u1, User2 = u2};
var f2 = new Friends() { User1 = u1, User2 = u3 };
context.Friends.Add(f1);
context.Friends.Add(f2);
context.SaveChanges();
}
}
}
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public virtual ICollection<Friends> FriendsCol { get; set; }
}
public class Friends
{
public int User1Id { get; set; }
public int User2Id { get; set; }
public User User1 { get; set; }
public User User2 { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Friends> Friends { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Friends>()
.HasKey(m => new { m.User1Id, m.User2Id });
modelBuilder.Entity<Friends>()
.HasRequired(x => x.User1)
.WithMany()
.HasForeignKey(x => x.User1Id)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Friends>()
.HasRequired(x => x.User2)
.WithMany(u => u.FriendsCol)
.HasForeignKey(x => x.User2Id)
.WillCascadeOnDelete(false);
}
}
Ok, here is what really should happen:
class Program
{
static void Main(string[] args)
{
int id1;
int id2;
using (var context = new Context())
{
context.Database.Delete();
context.Database.CreateIfNotExists();
var u1 = new User() { Name = "A" };
var u2 = new User() { Name = "B" };
var u3 = new User() { Name = "C" };
var f1 = new Friends() { User1 = u1, User2 = u2 };
var f2 = new Friends() { User1 = u1, User2 = u3 };
u1.FriendsCol.Add(f1);
u1.FriendsCol.Add(f2);
context.SaveChanges();
id1 = u1.Id;
id2 = u2.Id;
}
using (var context = new Context())
{
var u1 = context.Users.Find(id1);
var friendsToRemove = u1.FriendsCol.Where(f => f.User2.Id == id2).ToList();
foreach (var friend in friendsToRemove)
{
u1.FriendsCol.Remove(friend);
}
context.SaveChanges();
}
}
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Friends> FriendsCol { get; set; }
public User()
{
FriendsCol = new List<Friends>();
}
}
public class Friends
{
public int User1Id { get; set; }
public int User2Id { get; set; }
public User User1 { get; set; }
public User User2 { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Friends> Friends { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Friends>()
.HasKey(m => new { m.User1Id, m.User2Id });
modelBuilder.Entity<Friends>()
.HasRequired(x => x.User1)
.WithMany()
.HasForeignKey(x => x.User1Id);
modelBuilder.Entity<Friends>()
.HasRequired(x => x.User2)
.WithMany(u => u.FriendsCol)
.HasForeignKey(x => x.User2Id);
}
}
Here is another fail to delete related entities. And this is the error: *A relationship from the 'Order_Lines' AssociationSet is in the 'Deleted' state. Given multiplicity constraints, a corresponding 'Order_Lines_Target' must also in the 'Deleted' state.*
class Program
{
static void Main(string[] args)
{
int orderid1;
int Lineid2;
using (var context = new Context())
{
var u1 = new Order() { Name = "A" };
var l1 = new OrderLine() { Name = "L1" };
var l2 = new OrderLine() { Name = "L2" };
u1.Lines.Add(l1);
u1.Lines.Add(l2);
context.Orders.Add(u1);
context.SaveChanges();
Orderid1 = u1.Id;
Lineid2 = l2.Id;
}
using (var context = new Context())
{
var u1 = context.Orders.Find(Orderid1);
foreach (var item in u1.Lines)
{
if (item.Id == Lineid2)
{
u1.Lines.Remove(item);
break;
}
}
context.SaveChanges();
}
}
}
public class OrderLine
{
public int Id { get; set; }
public string Name { get; set; }
public Order Order { get; set; }
}
public class Order
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<OrderLine> Lines { get; set; }
public Order()
{
Lines = new List<OrderLine>();
}
}
public class Context : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<OrderLine> OrderLiness { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>().HasMany<OrderLine>(o => o.Lines).WithRequired(l => l.Order);
}
}