开发者

Inserting new record into linking table with linq to entities with POCO

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 Perso开发者_运维技巧nId)
{
    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?


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜