使用 POCO 将新记录插入到使用 linq toEntity 的链接表中

发布于 2024-12-09 10:31:05 字数 2446 浏览 0 评论 0原文

我有一个多对多关系的团队表和玩家表。有一个名为 TeamOnPlayer 的链接表。 EF 与 POCO 为团队实体生成名为 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.ObjectDisposeException}

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

编辑

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

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) 方法。

你怎么认为?可以吗?这是一种丑陋的方式吗?

I have a Team table and a Player table in many to many relationship. There is a linking table called TeamOnPlayer. EF with POCO generates navigation propertie called Person for the Team entity and also generates a nav. prop. called Team for the People entity.

I'm trying to insert a new record into the TeamOnPlayer table, but EF and POCO hides it. I tried to do this:

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

The GetTeam(TeamId) and GetPerson(PersonId) gets the right team and person:

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

When it tries to call the team.Person.Add(person) it throws an exception:

"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection." System.Exception {System.ObjectDisposedException}

Can anyone please show me the correct way?

Edit

Now I understand what the problem was, thanks to you. I was a bit confused about the using blocks you included. For example this:

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

Where should I put it?

I've done something else. I simply did this, and it worked fine.

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

The only problem is, that i coludn't reuse my GetPerson(int id) and GetTeam(int id) method.

What do you think? Is it okay? Is this an ugly way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

兮子 2024-12-16 10:31:05

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

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

因为这些上下文已经被释放,所以你会得到异常。

您应该稍微重新设计代码:不要在存储库方法 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();
    }
}

编辑

关于性能调整的一点:在这种特定情况下,延迟加载是不必要的,而且更令人不安。当您只想向集合添加一个额外的 Person 时,它会导致加载一个(可能很长)集合 team.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();
}

My guess is that you are working with lazy loading - your navigation properties Team.Person and Person.Team are marked as virtual in your entity classes. The result is that your methods GetTeam and GetPerson do not exactly return Team and Person objects but instances of dynamically created proxy classes which are derived from those entities. This dynamic proxy supports lazy loading which means that EF tries to load the navigation collections Team.Person and Person.Team when you access them for the first time. This happens in your AddPersonToTeam method when you call Add on these collections.

Now the problem is that the proxies are created within an context which you immediately dispose in your GetTeam and GetPerson methods (at the end of the using block). The proxies have stored a reference to this context internally and will use this context to load the navigation collections from the database.

Because these contexts are already disposed you get the exception.

You should redesign your code a bit: Don't create a new context in your repository methods GetTeam and GetPerson. You should instead use the same context for all operations: Retrieving the Team, retrieving the Person and adding the relationship. For example:

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

Another approach is to make your "Repository"/"Service" not static, inject the context into the constructor and then use this context throughout the repository. Then you don't need to pass in the context into every method. A rough sketch:

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

Edit

One little thing about performance tuning: In this specific case lazy loading is not necessary and more disturbing. It causes to load a (potentially long) collection team.Person when you want to add only one additional Person to the collection. You can switch off lazy loading for this particular operation (I refer to my second example):

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();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文