Mapping Many-To-Many Scenario

Brendan Rice's Avatar

Brendan Rice

04 Jul, 2010 02:24 PM via web

Thanks in advance to anyone that is helping.

I have [Projects] that have [Tasks], both [Projects] and [Tasks] can have a [Context]. If a [Context] name already exists then it is to be used for the Project/Task instead of creating a new one.

When I assign a Context to a Task I get the following error:
a different object with the same identifier value was already associated with the session: 8342e08c-3c77-490e-8b23-9da501880511, of entity: MyNameSpace.Context

Here is how I assign Contexts to Projects and Tasks:

ContextDto taskContextDto = null;

        ContextDto projectContextDto = null;

        if (context == null)
        {
            taskContextDto = new ContextDto { Name = _context };
            projectContextDto = taskContextDto;
        }
        else
        {
            taskContextDto = context;
            projectContextDto = context;
        }

        IProjectWcfService projectWcfService = _projectServiceFactory.Create();

        if (!string.IsNullOrEmpty(_task))
        {
            dto.Tasks.Add(new TaskDto { Name = _task });

            if (!string.IsNullOrEmpty(_context))
            {
                dto.Tasks[0].Contexts = new List<ContextDto> { taskContextDto };
            }
        }

        if (!string.IsNullOrEmpty(_context))
        {
            dto.Contexts.Add(projectContextDto);
        }

Here is what my mapping classes look like:

[TASK] public class TaskMap : IAutoMappingOverride

{
    public void Override(AutoMapping<Task> mapping)
    {
        mapping.Table("Tasks");
        //mapping.LazyLoad(SetAttribute("lazy", "false");

        // Evil assigned ID - use identity instead unless you're working with a legacy DB
        mapping.Id(x => x.Id, "TaskID")
            .GeneratedBy
            .GuidComb(); 

        mapping.Map(x => x.Name, "TaskName");

        mapping.Map(x => x.Done, "Done");

        mapping.References<Project>(x => x.Project, "ProjectID")
            .Fetch
            .Join();

        mapping.HasManyToMany<Context>(x => x.Contexts)
            .Table("TaskContexts")
            .ParentKeyColumn("TaskID")
            .ChildKeyColumn("ContextID")
            .Cascade.All()
            .AsBag();
    }
}

}

[PROJECT]

public sealed class ProjectMap : IAutoMappingOverride<Project>
{
    public void Override(AutoMapping<Project> mapping)
    {
        mapping.Table("Projects");

        // Evil assigned ID - use identity instead unless you're working with a legacy DB
        mapping.Id(x => x.Id, "ProjectID")
            .GeneratedBy
            .GuidComb();

        mapping.Map(x => x.Name, "ProjectName");

        mapping.Map(x => x.Done, "Done");

        mapping.References(x => x.User, "UserID");

        mapping.HasManyToMany<Context>(x => x.Contexts)
            .Table("ProjectContexts")
            .ParentKeyColumn("ProjectID")
            .ChildKeyColumn("ContextID")
            .Cascade.All()
            .AsBag();

        mapping.HasMany(x => x.Tasks)
            .KeyColumns.Add("ProjectID")
            .Inverse()
            .Cascade.All()
            .AsBag();
    }
}

[CONTEXT]

public class ContextMap : IAutoMappingOverride<Context>
{
    public void Override(AutoMapping<Context> mapping)
    {
        mapping.Table("Contexts");

        // Evil assigned ID - use identity instead unless you're working with a legacy DB
        mapping.Id(x => x.Id, "ContextID")
            .GeneratedBy
            .GuidComb(); 

        mapping.Map(x => x.Name, "ContextName");
    }
}

  1. Support Staff 2 Posted by Paul Batum on 05 Jul, 2010 12:03 PM

    Paul Batum's Avatar

    The error you are getting usually indicates that there is something wrong
    with the way you are handling your objects and sessions, rather than your
    mappings. Also, I'm having alot of trouble understanding the code you
    provided because you listed your maps, but the example code is all DTO's,
    not domain objects. Can you explain why?

  2. 3 Posted by Brendan Rice on 05 Jul, 2010 04:23 PM

    Brendan Rice's Avatar

    Hi Paul,

    thanks for the reply. Apologies I should have mentioned I am using S#arp Architecture for the server side code and all it does is open a session per request. I am hitting web services that open the session then everything after that is pretty standard S#arp Architecture i.e. repository.

    Any ideas?

    Thanks again,

    B

  3. 4 Posted by Brendan Rice on 05 Jul, 2010 10:29 PM

    Brendan Rice's Avatar

    Here is my server side code:

    SERVICE SAVE

        public ProjectDto Save(ProjectDto projectDto)
        {
            User user = CurrentUser;
    
            // I'd rather have the transaction begun via an attribute, like with a controller action, 
            // or within a service object, but this works for the current example.
            _projectRepository.DbContext.BeginTransaction();
    
            Project project = Project.CreateDomain(projectDto);
            project.User = user;
            project.Tasks.ForEach(x => x.Project = project);
    
            project.Validate();
    
            Project savedProject = project.IsNew ? _projectRepository.Save(project) : _projectRepository.Update(project);
    
            var returnDto = Project.CreateDto(savedProject);
            // Since we're certainly not going to require lazy loading, commit the transcation
            // before returning the data.
            _projectRepository.DbContext.CommitTransaction();
    
            return returnDto;
        }
    

    DOMAIN OBJECT

        public static ProjectDto CreateDto(Project project)
        {
            InitializeMapper();
    
            ProjectDto projectDto = Mapper.Map<Project, ProjectDto>(project);
    
            return projectDto;
        }
    
        public static Project CreateDomain(ProjectDto projectDto)
        {
            InitializeMapper();
    
            Project project = Mapper.Map<ProjectDto, Project>(projectDto);
    
            return project;
        }
    

    Again any help is appreciated.

    B

  4. Support Staff 5 Posted by Paul Batum on 06 Jul, 2010 11:18 AM

    Paul Batum's Avatar

    Nothing in that most recent batch of code strikes me as odd, but I still
    don't really understand where the exception is happening and how that first
    batch of code you provided relates to everything else.

  5. 6 Posted by Brendan Rice on 06 Jul, 2010 11:39 AM

    Brendan Rice's Avatar

    I think it may have something to do with adding a context twice although I am not sure.

    I really need to dig into it a little further, are there any good debugging tools out there for NH?

  6. Support Staff 7 Posted by Paul Batum on 06 Jul, 2010 12:11 PM

    Paul Batum's Avatar

    NH Prof is useful but its more for doing performance optimization than
    debugging.

    Usually the way I tackle a problem like this is I try to strip it down, make
    it simpler and simpler until it starts working and then backtrack.

  7. 8 Posted by Brendan Rice on 06 Jul, 2010 10:21 PM

    Brendan Rice's Avatar

    I think I have figured out what was going wrong. Let me explain.

    When I add a new Task to an existing Project and assign it an existing Context that already belong to the project the mapping gets confused as the Context is already assign to a Project and is already therefore in the session.

    How can I go about avoiding this?

  8. Support Staff 9 Posted by Paul Batum on 07 Jul, 2010 10:58 AM

    Paul Batum's Avatar

    For me personally, this problem is too difficult to remotely debug as its
    currently presented. If you can come up with a version that deals purely
    with domain objects and sessions (not dto's) and still exhibits the problem,
    I'll have a chance of making sense of it. If the problem only occurs in your
    full stack with DTO's in the mix, then I'd suggest that the problem is not
    related to your mappings.

Reply to this discussion

Preview Comments are parsed with Markdown. Help with syntax

Attached Files

    You can attach files up to 10MB

    Ten divided by two is what?