Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/258.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/backbone.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 代码优先实体框架。急于加载、验证然后保存导致错误_C#_Entity Framework 6_Ef Code First - Fatal编程技术网

C# 代码优先实体框架。急于加载、验证然后保存导致错误

C# 代码优先实体框架。急于加载、验证然后保存导致错误,c#,entity-framework-6,ef-code-first,C#,Entity Framework 6,Ef Code First,我的设置是,我有一个篮子,其中包含项目。项目由产品和尺寸组成。产品与尺寸之间存在多对多关系,因此我可以验证给定尺寸对给定产品是否有效。我希望能够添加一个项目到篮子,执行一些验证和保存到数据库 我创建了一个演示程序来演示我遇到的问题。当程序运行时,数据库中已经保存了一个篮子(请参阅DBInitializer)。它有一个项目是一个大foo。在程序中,你可以看到我装载了篮子,装载了一个小尺寸和一个棒材产品。我把那根大条子加到篮子里。篮子进行一些内部验证,我保存到数据库中。这是正确的 当我尝试添加数据库

我的设置是,我有一个篮子,其中包含项目。项目由产品和尺寸组成。产品与尺寸之间存在多对多关系,因此我可以验证给定尺寸对给定产品是否有效。我希望能够添加一个项目到篮子,执行一些验证和保存到数据库

我创建了一个演示程序来演示我遇到的问题。当程序运行时,数据库中已经保存了一个篮子(请参阅DBInitializer)。它有一个项目是一个大foo。在程序中,你可以看到我装载了篮子,装载了一个小尺寸和一个棒材产品。我把那根大条子加到篮子里。篮子进行一些内部验证,我保存到数据库中。这是正确的

当我尝试添加数据库中已经存在的具有不同大小的产品时,问题就出现了。因此,如果我们尝试向篮子中添加一个大条并保存,就会得到一个空引用异常。这不是我想要的行为,因为一个篮子包含两个项目,一个大foo和一个小foo,是完全有效的

我敢肯定,问题在于我们已经通过急切的装载将foo装载到了篮子里。我试着评论一下急切地装载篮子物品的情况,这很有效。然而,如果可能的话,我希望有一个解决方案,保持急切的加载

注意:我在dbcontext类中添加了一个额外的方法,即int SaveChanges(bool excludeReferenceData)。这将停止将额外的产品和尺寸记录保存回数据库。我已经公开了我所有的构造函数、getter和setter,以便更容易复制我的问题。我的演示代码是在一个以.net framework 4.5.2为目标的控制台应用程序上创建的。实体框架的版本是6.2

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using static Demo.Constants;

namespace Demo
{
    public static class Constants
    {
        public static int BasketId => 1;
        public static int SmallId => 1;
        public static int LargeId => 2;       
        public static int FooId => 1;
        public static int BarId => 2;
    }  

    public class Program
    {
        public static void Main()
        {
            using (var context = new AppContext())
            {
                var customerBasket = context.Baskets
                    .Include(b => b.Items.Select(cbi => cbi.Product))
                    .Include(b => b.Items.Select(cbi => cbi.Size))
                .SingleOrDefault(b => b.Id == BasketId);

                var size = context.Sizes.AsNoTracking()
                         .SingleOrDefault(s => s.Id == SmallId);    
                context.Configuration.ProxyCreationEnabled = false;
                var product = context
                    .Products
                    .AsNoTracking()
                    .Include(p => p.Sizes)
                    .SingleOrDefault(p => p.Id == BarId);  
                     //changing BarId to FooId in the above line results in 
                     //null reference exception when savechanges is called.

                customerBasket.AddItem(product, size);
                context.SaveChanges(excludeReferenceData: true);
            }
            Console.ReadLine();
         }
    }   

    public class Basket
    {
        public int Id { get; set; }
        public virtual ICollection<Item> Items { get; set; }

        public Basket()
        {
            Items = new Collection<Item>();
        }

        public void AddItem(Product product, Size size)
        {
            if (itemAlreadyExists(product, size))
            {
                throw new InvalidOperationException("item already in basket");
            }

            var newBasketItem = Item.Create(
              this,
              product,
              size);

            Items.Add(newBasketItem);    
        }

        private bool itemAlreadyExists(Product product, Size size)
        {
            return Items.Any(a => a.ProductId == product.Id && a.SizeId == size.Id);
        }
    }    

    public class Item
    {
        public Guid Id { get; set; }
        public int BasketId { get; set; }

        public virtual Product Product { get; set; }
        public int ProductId { get; set; }

        public virtual Size Size { get; set; }
        public int SizeId { get; set; }

        public Item()
        {

        }

        public string getDescription()
        {
            return $"{Product.Name} - {Size.Name}";
        }    

        internal static Item Create(Basket basket
            , Product product,
            Size size)
        {
            Guid id = Guid.NewGuid();

            if (!product.HasSize(size))
            {
                throw new InvalidOperationException("product does not come in size");
            }

            var basketItem = new Item
            {
                Id = id,
                BasketId = basket.Id,
                Product = product,
                ProductId = product.Id,
                Size = size,
                SizeId = size.Id
            };
            return basketItem;
        }
    }

    public class Product : IReferenceObject
    {
        public int Id { get; set; }
        public string Name { get;  set; }
        public virtual ICollection<ProductSize> Sizes { get;  set; }

        public Product()
        {
            Sizes = new Collection<ProductSize>();
        }

        public bool HasSize(Size size)
        {
            return Sizes.Any(s => s.SizeId == size.Id);
        }
    }

    public class ProductSize : IReferenceObject
    {
        public int SizeId { get;  set; }
        public virtual Size Size { get; set; }
        public int ProductId { get; set; }
    }    

    public class Size : IReferenceObject
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class AppContext : DbContext
    {
        public DbSet<Basket> Baskets { get; set; }
        public DbSet<Product> Products { get; set; }
        public DbSet<Size> Sizes { get; set; }          

        public AppContext()
           : base("name=DefaultConnection")
        {    
            Database.SetInitializer(new DBInitializer());
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Basket>()
            .HasMany(c => c.Items)
            .WithRequired()
            .HasForeignKey(c => c.BasketId)
            .WillCascadeOnDelete(true);

            modelBuilder.Entity<Item>()
            .Property(c => c.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

            modelBuilder.Entity<Item>()
            .HasKey(c => new { c.Id, c.BasketId });

            modelBuilder.Entity<ProductSize>()
                .HasKey(c => new { c.ProductId, c.SizeId });
            base.OnModelCreating(modelBuilder);

        }

        public int SaveChanges(bool excludeReferenceData)
        {
            if(excludeReferenceData)
            {
                var referenceEntries = 
                     ChangeTracker.Entries<IReferenceObject>()
                    .Where(e => e.State != EntityState.Unchanged 
                           && e.State != EntityState.Detached);

                foreach (var entry in referenceEntries)
                {
                   entry.State = EntityState.Detached;
                }
            }

            return SaveChanges();
        }
    }

    public interface IReferenceObject
    {
    }

    public class DBInitializer: DropCreateDatabaseAlways<AppContext>
    {
        protected override void Seed(AppContext context)
        {

            context.Sizes.Add(new Size { Id = LargeId, Name = "Large" });
            context.Sizes.Add(new Size { Id = SmallId, Name = "Small" });            

            context.Products.Add(
                new Product
                {
                    Id = FooId,
                    Name = "Foo",
                    Sizes = new Collection<ProductSize>()
                    {
                        new ProductSize{ProductId = FooId, SizeId = LargeId},
                        new ProductSize{ProductId = FooId, SizeId =SmallId}
                    }
                });

            context.Products.Add(new Product { Id = BarId, Name = "Bar",
                Sizes = new Collection<ProductSize>()
                    {
                        new ProductSize{ProductId = BarId, SizeId = SmallId}                        
                    }
            });

            context.Baskets.Add(new Basket
            {
                Id = BasketId,
                Items = new Collection<Item>()
                {
                    new Item
                    {
                        Id = Guid.NewGuid(),
                        BasketId =BasketId,
                        ProductId = FooId,
                        SizeId = LargeId
                    }
                }
            });    
            base.Seed(context);
        }
    } 
 }
使用系统;
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用System.ComponentModel.DataAnnotations.Schema;
使用System.Data.Entity;
使用System.Linq;
使用静态Demo.Constants;
名称空间演示
{
公共静态类常量
{
公共静态int BasketId=>1;
公共静态int SmallId=>1;
公共静态int-LargeId=>2;
公共静态intfooid=>1;
公共静态int BarId=>2;
}  
公共课程
{
公共静态void Main()
{
使用(var context=new AppContext())
{
var customerBasket=context.Baskets
.Include(b=>b.Items.Select(cbi=>cbi.Product))
.Include(b=>b.Items.Select(cbi=>cbi.Size))
.SingleOrDefault(b=>b.Id==BasketId);
var size=context.size.AsNoTracking()
.SingleOrDefault(s=>s.Id==SmallId);
context.Configuration.ProxyCreationEnabled=false;
var乘积=上下文
.产品
.AsNoTracking()
.包括(p=>p.尺寸)
.SingleOrDefault(p=>p.Id==BarId);
//将上面一行中的BarId更改为FooId会导致
//调用savechanges时出现空引用异常。
customerBasket.AddItem(产品、尺寸);
SaveChanges(excludeReferenceData:true);
}
Console.ReadLine();
}
}   
公共类篮子
{
公共int Id{get;set;}
公共虚拟ICollection项{get;set;}
公共篮子()
{
Items=新集合();
}
公共空白附加项(产品、尺寸)
{
if(itemAlreadyExists(产品、尺寸))
{
抛出新的InvalidOperationException(“项目已在篮子中”);
}
var newBasketItem=Item.Create(
这
产品,,
尺寸);
项目。添加(newBasketItem);
}
私有布尔项ReadyExists(产品、大小)
{
返回Items.Any(a=>a.ProductId==product.Id&&a.SizeId==size.Id);
}
}    
公共类项目
{
公共Guid Id{get;set;}
public int BasketId{get;set;}
公共虚拟产品产品{get;set;}
public int ProductId{get;set;}
公共虚拟大小{get;set;}
公共整数SizeId{get;set;}
公共项目()
{
}
公共字符串getDescription()
{
返回$“{Product.Name}-{Size.Name}”;
}    
内部静态项目创建(篮子)
,产品,
尺寸(尺寸)
{
Guid id=Guid.NewGuid();
如果(!product.HasSize(大小))
{
抛出新的InvalidOperationException(“产品没有大小”);
}
var basketItem=新项目
{
Id=Id,
BasketId=basket.Id,
产品=产品,
ProductId=product.Id,
尺寸=尺寸,
SizeId=size.Id
};
归还篮子物品;
}
}
公共类产品:IReferenceObject
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共虚拟ICollection大小{get;set;}
公共产品()
{
大小=新集合();
}
公共bool HasSize(尺寸)
{
返回大小.Any(s=>s.SizeId==size.Id);
}
}
公共类ProductSize:IReferenceObject
{
公共整数SizeId{get;set;}
公共虚拟大小{get;set;}
public int ProductId{get;set;}
}    
公共类大小:IReferenceObject
{
公共int Id{get;