使用linq将新记录插入到具有POCO的实体的链接表中

时间:2011-10-12 23:04:50

标签: entity-framework insert linker navigation

我有很多关系的Team表和Player表。有一个名为TeamOnPlayer的链接表。带有POCO的EF为团队实体生成名为Person的导航属性,并生成导航。支柱。称为Team for the People实体。

我正在尝试在TeamOnPlayer表中插入新记录,但EF和POCO隐藏了它。我试着这样做:

public static void AddPersonToTeam(int TeamId, int PersonId)
    {
        using (var ef = new korfballReportEntities())
        {
            var team = GetTeam(TeamId);
            var person = GetPerson(PersonId);

            team.Person.Add(person);
            person.Team.Add(team);

            ef.SaveChanges();
        }
    }

GetTeam(TeamId)和GetPerson(PersonId)获得合适的团队和人员:

public static Team GetTeam(int id)
    {
        using (var ef = new korfballReportEntities())
        {
            var q = from l in ef.Team
                    where l.Id == id
                    select l;
            return q.Single();
        }
    }
public static Person GetPerson(int id)
    {
        using (var ef = new korfballReportEntities())
        {
            var query = from p in ef.Person
                        where p.Id == id
                        select p;
            return query.Single();
        }
    }

当它试图调用team.Person.Add(person)时会抛出异常:

  

“ObjectContext实例已被释放,不能再用于需要连接的操作。” System.Exception {System.ObjectDisposedException}

任何人都可以告诉我正确的方法吗?

修改

现在我明白了问题所在,多亏了你。我对你包含的使用块感到有点困惑。例如:

using (var ef = new korfballReportEntities())
{
//switch lazy loading off, only in this single context
ef.Configuration.LazyLoadingEnabled = false;

var repository = new MyRepository(ef);
repository.AddPersonToTeam(int TeamId, int PersonId);
}

我应该把它放在哪里?

我做了别的事。我只是这样做了,而且效果很好。

public static void AddPersonToTeam(int TeamId, int PersonId)
{
    using (var ef = new korfballReportEntities())
    {
        var q = from t in ef.Team
                where t.Id == TeamId
                select t;
        var team =  q.Single();

        var q2 = from p in ef.Person
                where p.Id == PersonId
                select p;
        var person = q2.Single();

        try
        {
            team.Person.Add(person);
            person.Team.Add(team);
        }
        catch (Exception e)
        {  
        }

        ef.SaveChanges();
    }
}

唯一的问题是,我不能重用我的GetPerson(int id)和GetTeam(int id)方法。

你怎么看?好吗?这是一种丑陋的方式吗?

1 个答案:

答案 0 :(得分:0)

我的猜测是您正在使用延迟加载 - 导航属性Team.PersonPerson.Team在您的实体类中标记为virtual。结果是您的方法GetTeamGetPerson并不完全返回TeamPerson个对象,而是从这些实体派生的动态创建的代理类的实例。此动态代理支持延迟加载,这意味着当您第一次访问导航集时,EF会尝试加载导航集Team.PersonPerson.Team。当您在这些集合上调用AddPersonToTeam时,会在Add方法中发生这种情况。

现在的问题是代理是在一个上下文中创建的,您可以立即在GetTeamGetPerson方法(在使用块的末尾)中进行处理。代理在内部存储了对此上下文的引用,并将使用此上下文从数据库加载导航集合。

因为这些情境已被处理,所以你会得到例外。

您应该重新设计一下代码:不要在存储库方法GetTeamGetPerson中创建新的上下文。您应该为所有操作使用相同的上下文:检索Team,检索Person并添加关系。例如:

public static void AddPersonToTeam(int TeamId, int PersonId)
{
    using (var ef = new korfballReportEntities())
    {
        var team = GetTeam(ef, TeamId);
        var person = GetPerson(ef, PersonId);

        team.Person.Add(person);
        //person.Team.Add(team); <- not necessary, EF will handle this

        ef.SaveChanges();
    }
}

public static Team GetTeam(korfballReportEntities ef, int id)
{
    var q = from l in ef.Team
            where l.Id == id
            select l;
    return q.Single();
}

public static Person GetPerson(korfballReportEntities ef, int id)
{
    var query = from p in ef.Person
                where p.Id == id
                select p;
    return query.Single();
}

另一种方法是使“存储库”/“服务”不是静态的,将上下文注入构造函数,然后在整个存储库中使用此上下文。然后,您不需要将上下文传递给每个方法。粗略草图:

using (var ef = new korfballReportEntities())
{
    var repository = new MyRepository(ef);
    repository.AddPersonToTeam(int TeamId, int PersonId);
}

public class MyRepository
{
    private readonly korfballReportEntities _ef;
    public MyRepository(korfballReportEntities ef)
    {
        _ef = ef;
    }

    public void AddPersonToTeam(int TeamId, int PersonId)
    {
        var team = GetTeam(TeamId);
        var person = GetPerson(PersonId);

        team.Person.Add(person);

        _ef.SaveChanges();
    }

    public Team GetTeam(int id)
    {
        var q = from l in _ef.Team
                where l.Id == id
                select l;
        return q.Single();
    }

    public Person GetPerson(int id)
    {
        var query = from p in _ef.Person
                    where p.Id == id
                    select p;
        return query.Single();
    }
}

修改

性能调优的一个小问题:在这个特定情况下,延迟加载不是必需的,而且更令人不安。当您只想向集合中添加一个team.Person时,它会导致加载(可能很长)集合Person。您可以关闭此特定操作的延迟加载(我参考我的第二个示例):

using (var ef = new korfballReportEntities())
{
    //switch lazy loading off, only in this single context
    ef.Configuration.LazyLoadingEnabled = false;

    var repository = new MyRepository(ef);
    repository.AddPersonToTeam(int TeamId, int PersonId);
}

public void AddPersonToTeam(int TeamId, int PersonId)
{
    var team = GetTeam(TeamId);
    var person = GetPerson(PersonId);

    // if lazy loading is off, the collecton is null, so we must instantiate one
    if (team.Person == null)
        team.Person = new List<Person>();
    team.Person.Add(person);

    _ef.SaveChanges();
}