EF Code First Generic检查没有公共ID属性的对象的存在?

时间:2012-01-24 15:37:22

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

编辑:在本问题的底部回答

好的,所以我有一些通用的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,即使实体刚刚在内存中创建,因此插入实际上永远不会在Update方法中仅调用Save,我想这是因为我完全不了解DbEntityEntry,因为它本身和Entry.Entity都不是null。如何检查存在?

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);

是:

  

违反PRIMARY KEY约束'PK_ GroupTypes _00551192'。无法在对象'dbo.GroupTypes'中插入重复键。   声明已经终止。

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

1 个答案:

答案 0 :(得分:1)

  

如何检查存在?

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

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