简体   繁体   中英

EF Core - Update Entity

I would like to update DB via Entity Framework Core Product Entity.

On each load a product I would like to increment a Visited number.

Product Context Class:

[Table("product")]
public class Product
{
    [Key]
    [Column("id", TypeName = "int")]
    public int Id { get; set; }
    [Column("name")]
    public string Name { get; set; }
    [Column("active", TypeName = "bit")]
    public bool Active { get; set; }
    [Column("visited")]
    public int Visited { get; set; }

    // last group
    [Column("group_id")]
    public int GroupId { get; set; }
    [ForeignKey("GroupId")]
    public virtual Group Group { get; set; }

    // first group
    [Column("top_group_id")]
    public int? TopGroupId { get; set; }
    [ForeignKey("TopGroupId")]
    public virtual Group TopGroup { get; set; }

    // PN image
    [Column("file_image_id")]
    public int? ImageId { get; set; }

    [ForeignKey("ImageId")]
    public virtual File Image { get; set; }

    public virtual IEnumerable<ProductGrade> ProductGrades { get; set; }
    public virtual IEnumerable<ProductDescription> ProductDescriptions { get; set; }
    public virtual IEnumerable<ProductFile> ProductFiles { get; set; }
 }

In Controller, I call in CTOR _context:

// In class
private readonly ProductContext _context; 

// In method
var product = await _context.Products
                .OrderBy(b => b.Name)
                .Where(w => w.Name == model.Name)
                .Select(c => new ProductResDto
                {
                    Id = c.Id,
                    PartNumber = c.Name,
                    Group = c.Group.FullLink,
                    GroupId = c.GroupId,
                    Type = c.TopGroup.Name,
                    Image = new FileDto
                    {
                        Name = c.Image.Name,
                        GroupName = c.Image.FileGroup.Name,
                        Url = (c.Group.Top.Link != "/" ? c.Group.Top.Link : c.Group.Link) + "/pn/" + c.Image.Url,
                        Title = c.Image.Title,
                        Type = c.Image.FileType.MineType
                    }
                }).FirstAsync();

ProductResDto class:

public class ProductResDto
{
    [JsonIgnore] public int Id { get; set; }
    public string PartNumber { get; set; }
    public string Type { get; set; }
    public string Group { get; set; }
    [JsonIgnore] public int GroupId { get; set; }
    public ICollection<ParameterValueDto> Parameters { get; set; }
    public ICollection<ProductValueDto> Descriptions { get; set; }
    public ICollection<ProductValueDto> Grades { get; set; }
    public ICollection<FileGroupDto> Files { get; set; }
    public FileDto Image { get; set; }
    public ICollection<BreadCrumbDto> BreadCrumbs { get; set; }
    public RelatedProductResDto RelatedProducts { get; set; }
}

I would like to simply update product record like:

var data = await _context.Products
            .OrderBy(b => b.Name)
            .Where(w => w.Name == model.Name && w.Active).FirstAsync();

        var product = new ProductResDto
        {
            Id = data.Id,
            PartNumber = data.Name,
            Group = data.Group.FullLink,
            GroupId = data.GroupId,
            Type = data.TopGroup.Name,
            Image = new FileDto
            {
                Name = data.Image.Name,
                GroupName = data.Image.FileGroup.Name,
                Url = (data.Group.Top.Link != "/" ? data.Group.Top.Link : data.Group.Link) + "/pn/" + data.Image.Url,
                Title = data.Image.Title,
                Type = data.Image.FileType.MineType
            }
        };

        data.Visited += 1;

        _context.Products.Update(data);
        await _context.SaveChangesAsync();

I got only Error because variable data has only raw data pro DB Product. Is there any better way how to do it easier?

Like in the controller where I assign DB Context Class Product with all its dependencies to the ProductResDto?

Error:

System.NullReferenceException: 'Object reference not set to an instance of an object.'

In var product = new ProductResDto row

在此处输入图像描述

It loads only Product but without references, this is which I ask for simple way like in Controller where I use var product = await _context.Products

Thank you for any help

You need to add .Include() statements, because without them, EF Core will not load the related properties Group , TopGroup and Image (this is known as Lazy Loading) and as you can see in the image, these properties are indeed null.

It looks like you are also using deeper nested properties, which you can force to load using .ThenInclude() .

Here is how:

var data = await _context.Products
    .Include(p => p.Group).ThenInclude(g => g.Top)
    .Include(p => p.TopGroup)
    .Include(p => p.Image).ThenInclude(i => i.FileGroup)
    .Include(p => p.Image).ThenInclude(i => i.FileType)
    .OrderBy(b => b.Name)
    .Where(w => w.Name == model.Name && w.Active)
    .FirstAsync();

It's possible that this example is not yet 100% complete, in that case you need to add further .Include and/or .ThenInclude statements.

EF Core always analyzes the full LINQ query statement. Due to what is present in the .Select() statement in your first code block, EF Core sees that you want nested properties and it will create SQL that also loads these properties. But if there is no .Select() to give EF Core this information, then you need to add .Include() statements + possibly also .ThenInclude() statements.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM