2

Suppose you have the next class. It contains the systems in which the agent has worked

public class AgentHistory
{
    public ObjectId Id { get; set; }
    public Guid SystemId { get; set; }
    public Guid CampaignId { get; set; }
    public List<Agent> Agents { get; set; }
}

Now when I get a new agent I do the next thing:

 public override AgentHistory Save(AgentHistory agent)
    {
        if (agent == null)
            throw new ArgumentNullException("agent");
        if (_repository.Exists(agent))
        {
            AgentHistory dbEntity = _repository.FindById(agent.SystemId, agent.CampaignId);
            dbEntity.Agents.AddRange(agent.Agents);
            _repository.UpdateAgentHistory(dbEntity);
        }
        else
        {
            _repository.Save(agent);
        }
        return agent;
    }

And the next method in repository:

public void UpdateAgentHistory(AgentHistory updatedEntity)
    {
        QueryComplete query = Query.EQ("_id", BsonValue.Create(updatedEntity.Id));

        MongoCollection.Update(query, Update.Set("Agents", BsonArray.Create(updatedEntity.Agents)), UpdateFlags.None, SafeMode.True );
    }

I get the next exception .NET type Riverdale.Domain.BO.Agent cannot be mapped to a BsonValue. What am I doing wrong? What is the right approach to updating embedded collections?

Here is a simpler console app which throws(just as a demo):

  public class Agent
    {
        [BsonId]
        public string LocalIdentifier { get; set; }

        public string AgentName { get; set; }
    }

    public class A
    {
        public ObjectId Id { get; set; }
        public Guid SystemId { get; set; }
        public Guid CampaignId { get; set; }
        public Agent[] Agents { get; set; }
    }

    public class AgentHistoryRepository
    {
        public bool Exists(A agentHistory)
        {
            return _mongoCollection.FindOne(BuildIdentityQuery(agentHistory)) != null;
        }

        public void Delete(A agentHistory)
        {
            _mongoCollection.Remove(BuildIdentityQuery(agentHistory));
        }

        public List<string> GetAgentsForASystem(Guid systemGuid)
        {
            QueryComplete query = Query.EQ("SystemId", systemGuid);
            return _mongoCollection.Find(query).SelectMany(x => x.Agents.Select(z => z.AgentName)).Distinct().ToList();
        }

        public List<string> GetAgentsForACampaign(Guid systemGuid, Guid campaignGuid)
        {
            QueryComplete query = Query.EQ("CampaignId", campaignGuid);
            if (systemGuid != Guid.Empty)
                query = Query.And(new[] {query, Query.EQ("SystemId", systemGuid)});
            return _mongoCollection.Find(query).SelectMany(x => x.Agents.Select(z => z.AgentName)).Distinct().ToList();
        }

        public AgentHistoryRepository()
        {
            string connectionString = "mongodb://localhost/Sample";
            var mgsb = new MongoUrlBuilder(connectionString);
            var MongoServer = MongoDB.Driver.MongoServer.Create(mgsb.ToMongoUrl());
            var MongoDatabase = MongoServer.GetDatabase(mgsb.DatabaseName);
            _mongoCollection = MongoDatabase.GetCollection<A>("AgentHistory");
        }


        private MongoCollection<A> _mongoCollection;

        private QueryComplete BuildIdentityQuery(A agentHistory)
        {
            QueryComplete query = Query.And(Query.EQ("SystemId", agentHistory.SystemId),
                                            Query.EQ("CampaignId", agentHistory.CampaignId));
            return query;
        }

    public void Save(A entity)
        {
            _mongoCollection.Insert(entity, SafeMode.True);
        }

        public void UpdateAgents(A entity)
        {
            _mongoCollection.Update(BuildIdentityQuery(entity), Update.Set("Agents", entity.Agents.ToBsonDocument()));
        }
    }

    internal class Program
    {
        public static void Main()
        {

            var objectToSave =  new A {Id = ObjectId.GenerateNewId(), CampaignId=Guid.NewGuid(), SystemId =Guid.NewGuid() ,
                                     Agents = new [] {new Agent{LocalIdentifier="agent", AgentName= "name"}}};

            var repo = new AgentHistoryRepository();
            repo.UpdateAgents(objectToSave);
            objectToSave.Agents = new[] { new Agent { LocalIdentifier = "agent2", AgentName = "name2" } };
            repo.UpdateAgents(objectToSave);
            var objectToSave2 = new A
            {
                Id = ObjectId.GenerateNewId(),
                CampaignId = Guid.NewGuid(),
                SystemId = objectToSave.SystemId,
                Agents = new [] { new Agent { LocalIdentifier = "agent", AgentName = "name" } }
            };
            repo.UpdateAgents(objectToSave2);
            foreach (var agentName in repo.GetAgentsForASystem(objectToSave.SystemId))
                Console.WriteLine(agentName);
        }
    }
2
  • Can you show the Agent class? Commented Nov 11, 2011 at 10:52
  • public class Agent { public string LocalIdentifier { get; set; } public string AgentName { get; set; } } Commented Nov 11, 2011 at 10:53

1 Answer 1

1

You don't have to be so verbose: BsonValue.Create() and BsonArray.Create should not be required.

In fact, the latter is the cause of your problem: BsonArray.Create creates arrays of value types. You need an array of objects, however. If you take a look at the available overloads of BsonArray.Create, I guess you'll be invoking BsonArray.Create(IEnumerable), which is not desirable.

Have you tried to simply use

MongoCollection.Update(query, Update.Set("Agents", updatedEntity.Agents), ...);

instead?

In JSON, the difference looks like this:

Array of Values: [ val, val, ... ]

Array of Objects: [ { ... }, { ... }, ... ]

For example,

Simple Array: [ "mongodb", "awesomness", ... ]

Array of Objects: [ { userId: 2314234, comment: "Foo" }, { ... }, ... ]

Sign up to request clarification or add additional context in comments.

6 Comments

It still cannot map that type, anyhow the syntax you added is exactly what I wanted, thank you
I guess _mongoCollection.Update(BuildIdentityQuery(entity), Update.AddToSetEachWrapped("Agents", entity.Agents), SafeMode.True); should work, because Update.Set (also) does not accept any lists or documents. However, you cannot 'reset' the list, you can only add or remove. Alternatively, simply update the entire object or wrap the list in an object of its own, and use Update.SetWrapped.
Cool, it works. Could you explain me why does Update.Set not accept any lists or documents?
I don't know why it was designed this way; I assume that it is more complicated to serialize complex objects (i.e. slower). The way it is implemented now, it can guarantee good performance. Invocation of custom serializers, etc. probably plays a role, too.
Sometimes I get strange results like screencast.com/t/plkzxj2b7Sb7 . Can't really udnerstand the mechanics behind its work
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.