EF Core child ID doesn't match Database results - c#

I have the following classes:
[Table("Artikel_Meldung", Schema = "BDB")]
public class Artikel_Meldung
{
[Key]
public int Artikel_MeldungId { get; set; }
public int Meldung_Id { get; set; }
public Meldung Meldung { get; set; }
public Artikel Artikel { get; set; }
public long ArtNr { get; set; }
[Column(TypeName = "nvarchar(2)")]
public string Ausfkz { get; set; }
}
[Table("Meldung", Schema = "BDB")]
public class Meldung
{
[Key]
public int Meldung_Id { get; set; }
public string Erfasser { get; set; }
public Artikel ErsatzArtikel { get; set; }
[NotMapped]
public Artikel Parent { get; set; }
public long ErsatzArtNr { get; set; }
[Column(TypeName = "nvarchar(2)")]
public string ErsatzAusfKz { get; set; }
public virtual ICollection<Artikel_Meldung> Artikel_Meldungen { get; set; }
public string Kommentar { get; set; }
public DateTime StartZeitpunkt { get; set; }
public DateTime EndeZeitpunkt { get; set; }
}
And retrieving the results with the following command:
query = srv_apps_db.Artikel.Where(x => x.Artikelbezeichnung.Contains(search_string) || x.ModbezKd.Contains(search_string))
.Include(art => art.Supplier)
.Include(art => art.Warengruppe)
.Include(a => a.Inventory)
.Include(art => art.Specials.Where(spe => spe.GueltigAb <= DateTime.Now.Date && spe.GueltigBis >= DateTime.Now.Date && (spe.FilialNr == Filiale || spe.FilialNr == 0)))
.Include(art => art.Orders.Where(ord => ord.PosNr.Substring(0, 1) != "5"))
.Include(art => art.Artikel_Bilder).ThenInclude(img => img.Bild).ThenInclude(s => s.ImgTyp)
.Include(art => art.Artikel_Meldungen).ThenInclude(x=>x.Meldung);
artikel = await query.AsSplitQuery()
.AsNoTracking()
.ToListAsync();
Everything works so far, but as I inspected the retrievd objects I noticed that Meldung.Meldung_Id is always 1 off the actual value in the Database.
Json-Result of my API-Call:
{
"artikel_MeldungId": 608,
"meldung_Id": 609,
"meldung": {
"meldung_Id": 609,
"erfasser": "NZI",
"ersatzArtikel": {
"artNr": 10080405,
"ausfKz": "02",
....
}
}
The values are correct but the Id doesn't match is this a bug or an error in my code or expected behaviour on EF?
Thanks in advance
regards Tekkion

Related

Entity Framework 6 automatically changes entity value

I have two entities as shown in the screenshot:
each DIMPeriodDates can connect to many DIMPeriodComparatives and
each DIMPeriodComparatives can connect to many DIMPeriodDates
In other words, DIMPeriod can connect to themselves with order number.
This is the DIMPeriod class :
public class DIMPeriodDate
{
public enum EnumDIMPeriodPresentStatus
{
Refresh,
Operation
}
public enum EnumDIMPeriodType
{
Decisive,
Predicted
}
public enum EnumDIMPeriodAuditStatus
{
Audited,
NotAudited
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
public string Title { get; set; }
public string? Desc { get; set; }
public bool IsClosed { get; set; } = false;
[Column(TypeName = "date")]
public DateTime DateStart { get; set; }
[Column(TypeName = "date")]
public DateTime DateEnd { get; set; }
public List<DIMPeriodComparative> PeriodComparativeList { get; set; } = new();
public List<DIMPeriodComparative> PeriodBaseComparativeList { get; set; } = new();
}
And this is the PeriodComparative class :
public class DIMPeriodComparative
{
public int PeriodComparativeID { get; set; }
public int PeriodBaseID { get; set; }
public int Order { get; set; } = 1;
public DIMPeriodDate PeriodComparative { get; set; }
public DIMPeriodDate PeriodBase { get; set; }
}
Here is my Fluent API config :
modelBuilder.Entity<DIMPeriodComparative>()
.HasKey(q => new { q.PeriodComparativeID, q.PeriodBaseID });
modelBuilder.Entity<DIMPeriodComparative>()
.HasOne(q => q.PeriodComparative)
.WithMany(q => q.PeriodComparativeList)
.HasForeignKey(q=>q.PeriodComparativeID)
.OnDelete(DeleteBehavior.NoAction);
modelBuilder.Entity<DIMPeriodComparative>()
.HasOne(q => q.PeriodBase)
.WithMany(q => q.PeriodBaseComparativeList)
.HasForeignKey(q=>q.PeriodBaseID)
.OnDelete(DeleteBehavior.NoAction);
Now when I insert a new DIMPeriodComparatives entity to specific DIMPeriodDates like this :
After calling SaveChanges, the value automatically has changed :
PeriodBase and PeriodComparative have different Value with different id 11 and 13 ...
i change fulent api to this :
modelBuilder.Entity<DIMPeriodDate>()
.HasMany(q => q.PeriodComparativeBase)
.WithMany(q => q.PeriodComparative)
.UsingEntity<DIMPeriodComparative>(right =>
right.HasOne(q => q.PeriodComparative)
.WithMany()
.HasForeignKey(q => q.PeriodComparativeID)
, left =>
left.HasOne(q=>q.PeriodBase)
.WithMany()
.HasForeignKey(q=>q.PeriodBaseID)
, joinent =>
joinent.HasKey(q => new {q.PeriodComparativeID,q.PeriodBaseID})
);
and issue is fixed ...

Linq filtering by class array properties

My schema is as
I am currently getting the products with valiants like so
products = context.Products
.Include(x => x.Skus)
.Include(x => x.ProductVariants)
.ThenInclude(pv => pv.Option)
.Include(x => x.ProductVariants)
.ThenInclude(pv => pv.Value);
Now i am trying to add a filter functionality by OptionId and ValueId
The following list holds both the OptionId and the ValueId for every option selected at the UI
List<Filter> filters;
where Filter is
public class Filter
{
public int Oid { get; set; } //OptionId
public int Vid { get; set; } //ValueId
}
How could i add filter functionality on this one?
After using
var v = context.Products.Include(x => x.ProductVariants)
.Where(prod => prod.ProductVariants
.Any(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.ValueId)));
i got the error
The LINQ expression 'DbSet<ProductVariant>()
.Where(p0 => EF.Property<Nullable<int>>(EntityShaperExpression:
EntityType: Product
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
, "Id") != null && object.Equals(
objA: (object)EF.Property<Nullable<int>>(EntityShaperExpression:
EntityType: Product
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
, "Id"),
objB: (object)EF.Property<Nullable<int>>(p0, "ProductId")))
.Any(p0 => __filters_0
.Any(f => f.o == p0.OptionId && f.v == p0.ValueId))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Update
While using
var vv = context.ProductVariants
.Where(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.ValueId)).AsEnumerable();
The error is now
The LINQ expression 'DbSet<ProductVariant>()
.Where(p => __filters_0
.Any(f => f.Oid == p.OptionId && f.Vid == p.ValueId))' could not be translated.
Update
The error persists even with only filter by option
var vv = context.ProductVariants
.Where(v => filters.Any(f => f.Oid == v.OptionId)).AsEnumerable();
Update The classes used are
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public IEnumerable<ProductVariant> ProductVariants { get; set; }
public IEnumerable<Sku> Skus { get; set; }
}
public enum FilterType
{
CheckBox,
Radio,
Button,
List
}
public class Option
{
public int OptionId { get; set; }
public string OptionName { get; set; }
public FilterType FilterType { get; set; }
}
public class Value
{
public int ValueId { get; set; }
public string OptionValue { get; set; }
}
public class Sku
{
public int SkuId { get; set; }
public int ProductId { get; set; }
public decimal Price { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
}
public class ProductVariant
{
public int Id { get; set; }
public int ProductId { get; set; }
public int OptionId { get; set; }
public int ValueId { get; set; }
public int SkuId { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
[ForeignKey("OptionId")]
public Option Option { get; set; }
[ForeignKey("ValueId")]
public Value Value { get; set; }
[ForeignKey("SkuId")]
public Sku Sku { get; set; }
}
Update
I have narrowed the error to be related with the Filter class
By using
List<int> ints = new List<int>();
ints.Add(1);
and
var vv = context.ProductVariants
.Where(v => ints.Any(f => f == v.OptionId));
it just works. Should i use an expression or something else?
static Expression<Func<...
It'll likely end up looking like:
.Where(prod => prod.Variants.Any(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.VariantId)))
If your ProductVariants entity has no Id (not sure how you set up your entities), you might need f.Oid == v.Option.Id && f.Vid == v.Variant.Id
Unless you actually need to select fields out of Sku, Option and Value they could be dropped from the query; realistically only product and product variant are needed for the filtering
Edit:
Looks like the ORM is struggling to turn the linq into SQL. Starting from product variant might help:
context.ProductVariants
.Where(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.VariantId))
If that works out, add your product in
I wasn't able to replicate the problem with this code in a .net core winforms app and ef5:
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Linq;
using System.Windows.Forms;
namespace WFNetCoreCSWithEF5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var f = new List<Filter>() { new Filter { Oid = 1, Vid = 2 } };
var x = new ProductDbContext().ProductVariant.Where(pc => f.Any(f => f.Oid == pc.OptionId && f.Vid == pc.ValueId));
}
}
public class Filter
{
public int Oid { get; set; } //OptionId
public int Vid { get; set; } //ValueId
}
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public IEnumerable<ProductVariant> ProductVariants { get; set; }
//public IEnumerable<Sku> Skus { get; set; }
}
public enum FilterType
{
CheckBox,
Radio,
Button,
List
}
public class Option
{
public int OptionId { get; set; }
public string OptionName { get; set; }
public FilterType FilterType { get; set; }
}
public class Value
{
public int ValueId { get; set; }
public string OptionValue { get; set; }
}
public class Sku
{
public int SkuId { get; set; }
//public int ProductId { get; set; }
public decimal Price { get; set; }
//[ForeignKey("ProductId")]
//public Product Product { get; set; }
}
public class ProductVariant
{
public int Id { get; set; }
public int ProductId { get; set; }
public int OptionId { get; set; }
public int ValueId { get; set; }
public int SkuId { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
[ForeignKey("OptionId")]
public Option Option { get; set; }
[ForeignKey("ValueId")]
public Value Value { get; set; }
[ForeignKey("SkuId")]
public Sku Sku { get; set; }
}
public class ProductDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<ProductVariant> ProductVariant { get; set; }
public DbSet<Sku> Skus { get; set; }
public DbSet<Option> Options { get; set; }
public DbSet<Value> Values { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer(#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=c:\temp\new3.mdf;Integrated Security=True;Connect Timeout=30");
}
}
And these DOS commands:
dotnet ef migrations add X
dotnet ef database update
To create the db
Okay, ended up with PredicateBuilder
if (filters.Any())
{
var predicate = PredicateBuilder.False<Product>();
foreach (var filter in filters)
predicate = predicate.Or(p => p.ProductVariants.Any(x => x.OptionId == filter.o & x.ValueId == filter.v));
products = products.AsQueryable().Where(predicate);
}
products = products.AsQueryable()
.Include(x => x.ProductVariants)
.ThenInclude(pv => pv.Option)
.Include(x => x.ProductVariants)
.ThenInclude(x => x.Value);

Entity Framework get SUM from child property

I have the following model where I'd like to get the sum of all OrderTotalItems for all Orders of a Customer where the OrderTotalType (Enumeration) is "total" or 99:
public class Customer
{
...
public ICollection<Order> Orders { get; set; } = new Collection<Order>();
}
public class Order
{
...
public ICollection<OrderTotalItem> OrderTotalItems { get; set; } = new Collection<OrderTotalItem>();
}
public class OrderTotalItem
{
[Required]
public int Id { get; set; }
[Required]
[Column(TypeName = "decimal(10, 4)")]
public decimal Value { get; set; }
[Required]
public OrderTotalType Type { get; set; }
}
I am building a CustomerAdminDTO to include all relevant data of a customer for the admin client:
public class CustomerAdminDto
{
public int Id { get; set; }
public string UserId { get; set; }
public Gender Gender { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string VATId { get; set; } = "";
public bool VATIdValid { get; set; } = false;
public DateTime Added { get; set; }
public DateTime LastModified { get; set; }
public decimal OrdersTotal { get; set; }
public CustomerStatusShortDto CustomerStatus { get; set; }
public CustomerAddressDto CustomerAddress { get; set; }
public CustomerAddressDto BillingAddress { get; set; }
public ICollection<OrderListShortDto> Orders { get; set; }
}
In my data service I fill the DTO like that
var customerAdmin = await _context.Customers
.Include(x => x.Addresses)
.Include(x => x.CustomerStatus)
.Include(x => x.Orders)
.ThenInclude(x => x.OrderTotalItems)
.Where(x => x.UserId == userid)
.Select(customer => new CustomerAdminDto
{
Id = customer.Id,
UserId = customer.UserId,
Gender = customer.Gender,
FirstName = customer.FirstName,
LastName = customer.LastName,
VATId = customer.VATId,
VATIdValid = customer.VATIdValid,
Added = customer.Added,
LastModified = customer.LastModified,
OrdersTotal = customer.Orders.Sum(x => x.OrderTotalItems
.Where(x => x.Type == Enums.OrderTotalType.Total)
.Sum(x => x.Value)),
CustomerStatus = new CustomerStatusShortDto
{
Id = customer.CustomerStatus.Id,
Name = customer.CustomerStatus.Name,
},
...
}
.FirstOrDefaultAsync();
where everything works, except the OrdersTotal.
API compiles fine but throws the following error at runtime:
Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
Thanks for your hints!
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
This error in SQL server means that you tried to call aggregation function (customer.Orders.Sum() in your case) on other expression that contains aggregation function (.Sum(x => x.Value) in your case). In order to avoid this you can simplify your LINQ expression for OrdersTotal:
OrdersTotal = customer.Orders.SelectMany(o => o.OrderTotalItems).Where(x => x.Type == Enums.OrderTotalType.Total).Sum(x => x.Value)
There is only one aggregation here so it should work fine

Populate multiple collections on same type

I need to populate a Product object which contains two collections.
The current code works fine and populates the Product.GraphicItems collection, but I also need to populate the Product.InfoItems collection, but I can't figure out the syntax for multiple collections.
Current select:
var result = await this.Context.ShopCategorys
.Include(cat => cat.InfoItems)
.Include(cat => cat.Products)
.ThenInclude(prd => prd.GraphicItems)
.ThenInclude(itm => itm.Graphic)
.ThenInclude(gfx => gfx.Items)
.SingleAsync(cat => cat.Id.Equals(id));
Product.cs:
[Table("ShopProduct")]
public class Product : BaseShop
{
public bool Active { get; set; } = true;
public int CategoryId { get; set; }
public int CultureId { get; set; } = -1;
public List<ProductInfo> InfoItems { get; set; } = new List<ProductInfo>();
public List<ProductGraphic> GraphicItems { get; set; } = new List<ProductGraphic>();
}
ProductInfo.cs:
[Table("ShopProductInfo")]
public class ProductInfo : BaseShop, IInfoItem
{
public int? ProductId { get; set; }
public int CultureId { get; set; }
public Culture Culture { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Solution:
var result = await this.Context.ShopCategorys
.Include(cat => cat.InfoItems)
.Include(cat => cat.Products)
.ThenInclude(prd => prd.InfoItems)
.Include(cat => cat.Products)
.ThenInclude(prd => prd.GraphicItems)
.ThenInclude(itm => itm.Graphic)
.ThenInclude(gfx => gfx.Items)
.SingleAsync(cat => cat.Id.Equals(id));

How to write below mysql query using Entity Framework with Lambda expression?

SELECT
`Distinct1`.`UserId`,
`Distinct1`.`FirstName`,
`Distinct1`.`LastName`,
`Distinct1`.`EmailAddress`,
FROM ( SELECT DISTINCT
`Extent2`.`UserId`,
`Extent2`.`FirstName`,
`Extent2`.`LastName`,
`Extent2`.`EmailAddress`,
FROM `AssistantTo` AS `Extent1`
INNER JOIN `User` AS `Extent2` ON `Extent1`.`AssistantId` = `Extent2`.`UserId`
INNER JOIN `CustomTagUser` as `Extent3` ON `Extent3`.`UserId` = `Extent2`.`UserId`
WHERE `Extent1`.`OwnerId` = 274 AND `Extent3`.`CustomTagId` = 114
) AS `Distinct1`
This is my Table structure :
I tried using following query but it is giving me error.
var assistants =
dbContext.AssistantsTo
.Include(x => x.Assistant)
.Include(x => x.Assistant.CustomTagUser)
.Where(at =>
at.OwnerId == currentUser.UserId
&& (at.Assistant.CustomTagUser.Count(y => y.CustomTagId == filter) > 0)
)
.Select(at => at.Assistant)
.Distinct()
.ToList();
Error : {"Unknown column 'Extent1.AssistantId' in 'where clause'"}
Basically I have issue with giving filter for
`Extent3`.`CustomTagId` = 114
I think we can use any() but I have bad experience using any() with large data & mysql.
Model classes
public class AssistantTo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AssistantToId { get; set; }
public int AssistantId { get; set; }
[ForeignKey("AssistantId")]
public virtual User Assistant { get; set; }
public int OwnerId { get; set; }
[ForeignKey("OwnerId")]
public virtual User Owner { get; set; }
}
public class CustomTagUser
{
[Key]
public int CustomTagUserId { get; set; }
public int CustomTagId { get; set; }
public int UserId { get; set; }
public DateTime CreatedOn { get; set; }
[ForeignKey("CustomTagId")]
public virtual CustomTags CustomTags { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }
}
[Table("User")]
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
.....
public ICollection<CustomTagUser> CustomTagUser { get; set; }
}
}
Basically I have main problem with applying this part in EF
AND Extent3.CustomTagId = 114
var assistants =
dbContext.AssistantsTo
.Include(x => x.Assistant)
.Include(x => x.Assistant.CustomTagUser)
.Where(at =>
at.OwnerId == currentUser.UserId &&
(
at.Assistant.CustomTagUser.Select(x => x.CustomTagId).Contains(filter)
)
.Select(at => at.Assistant)
.Distinct()
.ToList();
try this one...

Categories

Resources