【问题标题】:EF Code First Generic check Existence of object without common ID Property?EF Code First 通用检查是否存在没有公共 ID 属性的对象?
【发布时间】:2012-02-17 20:38:48
【问题描述】:

编辑:在这个问题的底部回答

好的,我有一些通用的 EF 函数(其中大部分是从这里得到的),但它们似乎不起作用。

我有 3 节课:

 public class Group : Entity
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public virtual GroupType GroupType { get; set; }

    public virtual ICollection<User> Users { get; set; }
}
 public class GroupType: Entity
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

 public class User: Entity
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
    public string UserName { get; set; }

    public virtual ICollection<Group> Groups { get; set; }
 }

我的 CRUD 操作:

public void Insert(TClass entity)
    {
        if (_context.Entry(entity).State == EntityState.Detached)
        {
            _context.Set<TClass>().Attach(entity);
        }
        _context.Set<TClass>().Add(entity);
        _context.SaveChanges();
    }

public void Update(TClass entity)
    {
        DbEntityEntry<TClass> oldEntry = _context.Entry(entity);

        if (oldEntry.State == EntityState.Detached)
        {
            _context.Set<TClass>().Attach(oldEntry.Entity);
        }

        oldEntry.CurrentValues.SetValues(entity);
        //oldEntry.State = EntityState.Modified;

        _context.SaveChanges();
    }

public bool Exists(TClass entity)
    {
        bool exists = false;

        if(entity != null)
        {
            DbEntityEntry<TClass> entry = _repository.GetDbEntry(entity);
            exists = entry != null;
        }

        return exists;
    }

public void Save(TClass entity)
    {
        if (entity != null)
        {
            if (Exists(entity))
                _repository.Update(entity);
            else
                _repository.Insert(entity);
        }
    }

最后我用下面的方法调用这段代码:

public string TestCRUD()
    {

        UserService userService = UserServiceFactory.GetService();
        User user = new User("Test", "Test", "Test", "TestUser") { Groups = new Collection<Group>() };

        userService.Save(user);
        User testUser = userService.GetOne(x => x.UserName == "TestUser");

        GroupTypeService groupTypeService = GroupTypeServiceFactory.GetService();
        GroupType groupType = new GroupType("TestGroupType2", null);

        groupTypeService.Save(groupType);

        GroupService groupService = GroupServiceFactory.GetService();
        Group group = new Group("TestGroup2", null) { GroupType = groupType };
        groupService.Save(group);

        user.Groups.Add(group);
        userService.Save(user);

        return output;
    }

当我到达:

 user.Groups.Add(group);
 userService.Save(user);

我收到以下错误:

保存不为其关系公开外键属性的实体时发生错误。 EntityEntries 属性将返回 null,因为无法将单个实体标识为异常源。通过在实体类型中公开外键属性,可以更轻松地在保存时处理异常。有关详细信息,请参阅 InnerException。

有以下内部异常:

INSERT 语句与 FOREIGN KEY 约束“User_Groups_Source”冲突。冲突发生在数据库“DBNAME”、表“dbo.Users”、列“Id”中。

问题:

1) Exists 始终返回 true,即使该实体刚刚在内存中创建,因此 insert 实际上从未在 Save 方法内仅被调用 Update,我想这是因为我不了解 DbEntityEntry完全是因为它本身和 Entry.Entity 都永远不会为空。如何检查是否存在?

2) 尽管所有代码都在 TestCRUD 中运行到最后,但实际上并没有将这些实体保存到数据库中。我很肯定我的数据库设置正确,因为我的自定义初始化程序总是删除和重新创建数据库,并且每次都插入种子数据。这可能是因为更新总是被调用,如数字 1 中所述。

任何想法如何解决?

编辑:答案

正如假设的那样,问题是 Exists 总是返回 true,因此永远不会调用 insert。我通过使用反射来获取主键并将其插入到 find 方法中来解决这个问题,如下所示:

public bool Exists(TClass entity)
    {
        bool exists = false;

        PropertyInfo info  = entity.GetType().GetProperty(GetKeyName());
        if (_context.Set<TClass>().Find(info.GetValue(entity, null)) != null)
            exists = true;

        return exists;
    }

所有其他方法都按预期开始工作。但是,我在同一行遇到了不同的错误:

user.Groups.Add(group);
userService.Save(user);

原来是:

违反主键约束“PK_GroupTypes_00551192”。无法在对象“dbo.GroupTypes”中插入重复键。 声明已终止。

我将把它作为一个新问题发布,因为它是一个新错误,但它确实解决了第一个问题。

【问题讨论】:

  • 将实体附加到上下文后,您应该将其状态设置为添加或修改。请参阅 ObjectStateManager.ChangeObjectState 方法。但是在保存用户之前不能说为什么你的 Exists 方法会给你真实的。
  • @ElDog 我试过了(你可以看到它在更新中被注释掉了),它抛出了一个错误,说不能这样做,因为实体在操作之间被修改或删除或其他什么效果。

标签: c# asp.net-mvc-3 entity-framework ef-code-first crud


【解决方案1】:

如何检查是否存在?

我通常看到人们检查实体是否已经存在的方法是检查其Id 属性是否大于零。您应该能够使用带有 Id 属性的接口,或者(如果您希望拥有具有多个关键属性的实体)您可以让每个实体覆盖抽象基类,覆盖 Exists 属性以检查其拥有非默认值的 ID 属性。或者您可以使用反射自动查找 ID 属性或属性,as explained here

我怀疑一旦您正确插入新项目而不是尝试更新它们,其余问题就会消失。

【讨论】:

  • 没有其他方法可以通用吗?是的,我在这里发布了这个确切的问题:stackoverflow.com/questions/8948815/… 并尝试了答案,但似乎没有用。
  • 问题是一些实体有一个 int ID,prop 名称是 classnameId,而其他实体是 guid,prop 名称是 Id。要在泛型方法中使用任何 ID,他们必须实现相同的基类/接口,但是如何创建一个涵盖两者的接口呢?
  • 嗯好的,我看看能不能用它来做点什么。
  • 实际上我认为这也行不通,因为具有 GUID 属性作为 ID 的对象在创建时设置。有没有办法在 LINQ 查询中做一个完整的对象比较器来表示类似 .Where(x => x.Equals(entity), 除了 equals 显然不起作用但有类似的东西可以通用吗?
  • 我解决了这个问题,但现在遇到了另一个问题。不过我会发布一个新问题,因为这个问题足够长,而且我确实解决了原来的问题。
猜你喜欢
  • 2014-07-30
  • 2014-01-20
  • 1970-01-01
  • 2022-07-18
  • 2011-12-20
  • 1970-01-01
  • 2023-04-09
  • 2013-06-02
  • 1970-01-01
相关资源
最近更新 更多