C# HasRequired和WithOptional不生成一对一关系
我有两个非常简单的对象:C# HasRequired和WithOptional不生成一对一关系,c#,entity-framework,visual-studio-2017,entity-framework-6,fluent,C#,Entity Framework,Visual Studio 2017,Entity Framework 6,Fluent,我有两个非常简单的对象: public class GenericObject { public int Id { get; set; } public string description { get; set; } public User UserWhoGeneratedThisObject { get; set; } } public class User { public int Id { get; set; } public string
public class GenericObject
{
public int Id { get; set; }
public string description { get; set; }
public User UserWhoGeneratedThisObject { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Lastname { get; set; }
}
我正在重写OnModelCreating函数以声明对象的关系:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<GenericObject>().HasRequired(u => u.UserWhoGeneratedThisObject)
.WithOptional();
}
当我将myObject添加到数据库时,我是否应该遇到异常,因为我没有为它分配用户对象?(注释掉的行)我希望在这种情况下看到异常,但代码工作正常。更糟糕的是,如果我在ctx.SaveChanges()之后添加以下两行:
我发现变量u(GenericObject)实际上指向我在数据库中创建的用户,尽管我没有将用户分配给GenericObject。您需要设置两个导航属性:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<GenericObject>()
.HasRequired(genericObject => genericObject.UserWhoGeneratedThisObject)
.WithOptional(user => user.GenericObject);
}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
modelBuilder.Entity()
.HasRequired(genericObject=>genericObject.UserWhoGeneratedThis对象)
.WithOptional(用户=>user.generiObject);
}
您应该阅读:
守则:
using (var ctx = new Tests6Context()) {
GenericObject myObject = new GenericObject();
myObject.description = "testobjectdescription";
User usr = new User() { Name = "Test" };
ctx.Users.Add(usr);
//myObject.UserWhoGeneratedThisObject = usr;
ctx.GenericObjects.Add(myObject);
myObject = new GenericObject();
myObject.description = "testobjectdescription 2";
usr = new User() { Name = "Test 2" };
ctx.Users.Add(usr);
//myObject.UserWhoGeneratedThisObject = usr;
ctx.GenericObjects.Add(myObject);
ctx.SaveChanges();
}
抛出:
System.Data.Entity.Infrastructure.DbUpdateException:无法确定“ef6tests.GenericObject_UserWhoGeneratedThisObject”关系的主端。多个添加的实体可能具有相同的主键
在简单情况下,一个新用户和一个新GenericObject EF推断出处于相同状态的两个实体之间可能存在的唯一关系。在其他情况下,他抛出
当我将myObject添加到数据库时,我是否应该遇到异常,因为我没有为它分配用户对象?(注释掉的行)
我认为不会,因为“OnModelCreating”函数实现没有在数据库中创建一对一关系。
应创建将由“[ForeignKey]”属性标记为外键的GenericObject属性,以在“User”类中创建一对一关系。“GenericObject”属性应添加到“User”类中,并用虚拟修饰符标记。现在,“OnModelCreating”的实现将配置这些类之间的关系。
以下是模型示例:
public class User
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Lastname { get; set; }
[ForeignKey("GenericObject")]
public int GenericObjectId { get; set; }
public virtual GenericObject GenericObject { get; set; }
}
public class GenericObject
{
[Key]
public int Id { get; set; }
public string Description { get; set; }
public virtual User UserWhoGeneratedThisObject { get; set; }
}
应首先创建泛型对象,并将创建的对象id添加到新用户以避免此异常(若将外键放入用户,则会出现这种情况)
希望我能清楚地回答你的问题。TL;DR
这种行为并不特定于一对一
关系,一对多
的行为方式完全相同
如果要使EF对当前代码引发异常,请为generiobject
modelBuilder
.Entity<GenericObject>()
.HasRequired(u => u.UserWhoGeneratedThisObject)
.WithOptional()
.Map(config =>
{
config.MapKey("UserId");
});
配置
modelBuilder
.Entity<GenericObject>()
.HasRequired(u => u.UserWhoGeneratedThisObject)
.WithOptional();
modelBuilder
.Entity<GenericObject>()
.HasRequired(o => o.UserWhoGeneratedThisObject)
.WithMany()
.HasForeignKey(o => o.UserId);
modelBuilder
.Entity<GenericObject>()
.HasRequired(u => u.UserWhoGeneratedThisObject)
.WithOptional()
.Map(config =>
{
config.MapKey("UserId");
});
执行查询
INSERT [dbo].[GenericObjects]([Id], [Description])
VALUES (@0, @1)
-- @0: '0' (Type = Int32)
-- @1: 'some description' (Type = String, Size = -1)
例外情况
INSERT语句与外键约束“FK_dbo.GenericObjects_dbo.UserObjects_Id”冲突。冲突发生在数据库“TestProject”、表“dbo.UserObjects”、列“Id”中。
声明已终止
由于generiobject
是EF执行insert
查询的唯一实体,因此查询失败,因为数据库中没有Id
等于0
的UserObject
测试2。创建1个UserObject和一个GenericObject
var context = new AppContext();
GenericObject myObject = new GenericObject
{
Description = "some description"
};
context.GenericObjects.Add(myObject);
context.SaveChanges();
var context = new AppContext();
GenericObject myObject = new GenericObject
{
Description = "some description"
};
UserObject user = new UserObject
{
Name = "user"
};
context.UserObjects.Add(user);
context.GenericObjects.Add(myObject);
context.SaveChanges();
var context = new AppContext();
GenericObject myObject = new GenericObject
{
Description = "some description"
};
UserObject user = new UserObject
{
Name = "user"
};
UserObject user2 = new UserObject
{
Name = "user"
};
context.UserObjects.Add(user);
context.UserObjects.Add(user2);
context.GenericObjects.Add(myObject);
context.SaveChanges();
执行的查询
INSERT [dbo].[UserObjects]([Name])
VALUES (@0)
SELECT [Id]
FROM [dbo].[UserObjects]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
-- @0: 'user' (Type = String, Size = -1)
INSERT [dbo].[GenericObjects]([Id], [Description])
VALUES (@0, @1)
-- @0: '10' (Type = Int32)
-- @1: 'some description' (Type = String, Size = -1)
INSERT [dbo].[UserObjects]([Name])
VALUES (@0)
SELECT [Id]
FROM [dbo].[UserObjects]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
-- @0: 'user' (Type = String, Size = -1)
-- Executing at 14.03.2019 18:52:35 +02:00
INSERT [dbo].[GenericObjects]([Description], [UserId])
VALUES (@0, @1)
SELECT [Id]
FROM [dbo].[GenericObjects]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
-- @0: 'some description' (Type = String, Size = -1)
-- @1: '3' (Type = Int32)
现在上下文包含UserObject
(Id=0)和generiobject
(Id=0)。EF认为generiobject
引用了UserObject
,因为它的外键等于0,UserObject
主键等于0。因此,首先EF插入<代码> USER对象< /代码>作为主体,因为它考虑了代码>通用对象/<代码>,依赖于该用户,它使用返回<代码>用户对象.ID/COD>并执行第二个插入,并且一切都很好。
测试3。创建2个UserObject和一个GenericObject
var context = new AppContext();
GenericObject myObject = new GenericObject
{
Description = "some description"
};
context.GenericObjects.Add(myObject);
context.SaveChanges();
var context = new AppContext();
GenericObject myObject = new GenericObject
{
Description = "some description"
};
UserObject user = new UserObject
{
Name = "user"
};
context.UserObjects.Add(user);
context.GenericObjects.Add(myObject);
context.SaveChanges();
var context = new AppContext();
GenericObject myObject = new GenericObject
{
Description = "some description"
};
UserObject user = new UserObject
{
Name = "user"
};
UserObject user2 = new UserObject
{
Name = "user"
};
context.UserObjects.Add(user);
context.UserObjects.Add(user2);
context.GenericObjects.Add(myObject);
context.SaveChanges();
例外情况
EntityFramework.dll中出现“System.Data.Entity.Infrastructure.DbUpdateException”
其他信息:无法确定“TestConsole.Data.GenericObject_UserWhoGeneratedThisObject”关系的主体端。多个添加的实体可能具有相同的主键
EF发现在Id
等于0
和generiobject.Id
等于0的上下文中有2个UserObject
,因此框架无法明确连接实体,因为存在多个可能的选项
一对多
public class GenericObject
{
public int Id { get; set; }
public string Description { get; set; }
public int UserId { get; set; } //this property is essential to illistrate the problem
public UserObject UserWhoGeneratedThisObject { get; set; }
}
public class UserObject
{
public int Id { get; set; }
public string Name { get; set; }
}
配置
modelBuilder
.Entity<GenericObject>()
.HasRequired(u => u.UserWhoGeneratedThisObject)
.WithOptional();
modelBuilder
.Entity<GenericObject>()
.HasRequired(o => o.UserWhoGeneratedThisObject)
.WithMany()
.HasForeignKey(o => o.UserId);
modelBuilder
.Entity<GenericObject>()
.HasRequired(u => u.UserWhoGeneratedThisObject)
.WithOptional()
.Map(config =>
{
config.MapKey("UserId");
});
执行的查询非常类似于one-to-one
test2。唯一的区别是现在GenericObject
有单独的UserId
外键。推理也是如此。上下文包含UserObject
实体和Id
相等的0
和generiobject
和UserId
相等的UserObject
,因此EF认为它们已连接并执行UserObject
的插入,然后获取UserObject.Id
并执行第二次插入
测试3。创建2个UserObject和一个GenericObject
var context = new AppContext();
GenericObject myObject = new GenericObject
{
Description = "some description"
};
context.GenericObjects.Add(myObject);
context.SaveChanges();
var context = new AppContext();
GenericObject myObject = new GenericObject
{
Description = "some description"
};
UserObject user = new UserObject
{
Name = "user"
};
context.UserObjects.Add(user);
context.GenericObjects.Add(myObject);
context.SaveChanges();
var context = new AppContext();
GenericObject myObject = new GenericObject
{
Description = "some description"
};
UserObject user = new UserObject
{
Name = "user"
};
UserObject user2 = new UserObject
{
Name = "user"
};
context.UserObjects.Add(user);
context.UserObjects.Add(user2);
context.GenericObjects.Add(myObject);
context.SaveChanges();
与一对一
中的代码相同。它执行相同的查询并产生相同的异常。原因是一样的
从这些测试中可以看出,问题并非特定于一对一关系
但是,如果我们不将UserId
添加到GenericObject
,该怎么办?在这种情况下,EF将为我们生成userwhogeneratedthis object_Id
外键,现在数据库中有外键,但没有映射到它的属性。在这种情况下,每个测试都会立即抛出以下异常
using (var ctx = new TestContext())
{
GenericObject myObject = new GenericObject();
myObject.description = "testobjectdescription";
User usr = new User() { Name = "Test"};
ctx.Users.Add(usr);
//myObject.UserWhoGeneratedThisObject = usr;
ctx.GenericObjects.Add(myObject);
ctx.SaveChanges();
}
“AppContext.GenericObjects”中的实体参与“GenericObject_UserWhoGeneratedThisObject”关系。找到0个相关的“GenericObject\u Userwho生成此对象\u目标”。应为1“GenericObject\u Userwho生成此对象\u目标的用户”
为什么会发生这种情况?现在EF无法确定是否连接了generiobject
和UserObject