Creating MongoDB Unique Key with C#

后端 未结 6 1563
故里飘歌
故里飘歌 2020-12-29 03:08

I am fighting to create a unique field EmailAddress. I\'ve already seen in forums that I have to create an index, but it didn\'t work out for me so far. Does an

相关标签:
6条回答
  • 2020-12-29 03:45

    here's a working program using MongoDB.Entities (disclaimer: I'm the author)

    using System;
    using MongoDB.Driver;
    using MongoDB.Entities;
    
    namespace StackOverflow
    {
        public class User : Entity
        {
            public string Name { get; set; }
            public string EmailAddress { get; set; }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                new DB("test");
    
                DB.Index<User>()
                  .Options(o => o.Unique = true, o => o.Background = false)
                  .Key(u => u.EmailAddress, Type.Ascending)
                  .Create();
    
                var user1 = new User { Name = "First User", EmailAddress = "email@domain.com" };
                user1.Save();
    
                try
                {
                    var user2 = new User { Name = "Second User", EmailAddress = "email@domain.com" };
                    user2.Save();
                }
                catch (MongoWriteException x)
                {
                    Console.WriteLine(x.Message);
                    Console.ReadKey();
                }
            }
        }
    }
    

    trying to create a second user with the same email address results in the following exception:

    A write operation resulted in an error. E11000 duplicate key error collection: test.User index: EmailAddress(Asc) dup key: { : "email@domain.com" }

    0 讨论(0)
  • 2020-12-29 03:47

    As of 2.8 below is the way to create a index.Please note last two lines. CreateOneAsync(indexDefinition, options) is obsolete.

    var mongoClient = new MongoClient("connection");
    var db = mongoClient.GetDatabase("database");
    
    var options = new CreateIndexOptions() { Unique = true };
    var field = new StringFieldDefinition<User>("EmailAddress");
    var indexDefinition = new IndexKeysDefinitionBuilder<User>().Ascending(field);
    
    var indexModel = new CreateIndexModel<User>(indexDefinition,options);
    await db.GetCollection<Users>("users").Indexes.CreateOneAsync(indexModel);
    
    0 讨论(0)
  • 2020-12-29 03:48

    EnsureIndex() is deprecated/obsolete per the C# mongo drivers version 2.0 spec: http://api.mongodb.org/csharp/current/html/M_MongoDB_Driver_MongoCollection_EnsureIndex_2.htm

    heres how to do it async and via 2.0 code:

    var mongoClient = new MongoClient("connection");
    var db = mongoClient.GetDatabase("database");
    
    var options = new CreateIndexOptions() { Unique = true };
    var field = new StringFieldDefinition<User>("EmailAddress");
    var indexDefinition = new IndexKeysDefinitionBuilder<User>().Ascending(field);
    await db.GetCollection<Users>("users").Indexes.CreateOneAsync(indexDefinition, options);
    

    In case of a non-string index:

       var options = new CreateIndexOptions() { Unique = true };
       IndexKeysDefinition<Foo> keyCode = "{ Code: 1 }";
       var codeIndexModel = new CreateIndexModel<Foo>(keyCode, options);
    
    0 讨论(0)
  • 2020-12-29 03:56

    Your code looks right. Here's a full running program for you to compare against:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using MongoDB.Bson;
    using MongoDB.Driver;
    using MongoDB.Driver.Builders;
    
    namespace TestEnsureIndexOnEmailAddress {
        public class User {
            public ObjectId Id;
            public string FirstName;
            public string LastName;
            public string EmailAddress;
        }
    
        public static class Program {
            public static void Main(string[] args) {
                var server = MongoServer.Create("mongodb://localhost/?safe=true");
                var database = server["test"];
                var users = database.GetCollection<User>("users");
                if (users.Exists()) { users.Drop(); }
    
                users.EnsureIndex(IndexKeys.Ascending("EmailAddress"), IndexOptions.SetUnique(true));
                var john = new User { FirstName = "John", LastName = "Smith", EmailAddress = "jsmith@xyz.com" };
                users.Insert(john);
                var joe = new User { FirstName = "Joe", LastName = "Smith", EmailAddress = "jsmith@xyz.com" };
                users.Insert(joe); // should throw exception
            }
        }
    }
    

    You can also use the mongo shell to confirm that the index got created:

    > db.users.getIndexes()
    [
            {
                    "name" : "_id_",
                    "ns" : "test.users",
                    "key" : {
                            "_id" : 1
                    },
                    "v" : 0
            },
            {
                    "_id" : ObjectId("4de8152ee447ad2550e3b5fd"),
                    "name" : "EmailAddress_1",
                    "ns" : "test.users",
                    "key" : {
                            "EmailAddress" : 1
                    },
                    "unique" : true,
                    "v" : 0
            }
    ]
    >
    
    0 讨论(0)
  • 2020-12-29 04:05

    In my opinion a better and more feasible way to do this is by creating a custom attribute, then with c# you could get the properties decorated with that attribute and add a unique index, this approach would be generic and very simple to use. Example below:

    My custom attribute: MongoUniqueStringIndex

     public class DynamicFieldConfig
    {
        public string InputType { get; set; }
        [MongoUniqueStringIndex]
        public string Name { get; set; }
        public string Label { get; set; }
        public List<string> Options { get; set; } = new List<string>();
        public string LookupTypeName { get; set; }
        public int Type { get; set; } //int to enum
        public string Value { get; set; }
        public List<DynamicFieldValidation> Validations { get; set; } = new List<DynamicFieldValidation>();
    }
    

    then where you get the collection, for example in my Data Access class I do this:

            public MongoDataAccess(IDatabaseSettings settings)
        {
            // todo: create a connection service/factory to get connection
            var client = new MongoClient(settings.ConnectionString);
            var database = client.GetDatabase(settings.DatabaseName);
            
            var entityName = new Pluralizer().Pluralize(typeof(TMongoEntity).Name);
            _entityStore = database.GetCollection<TMongoEntity>(entityName);
    
            var uniqueStringIndexProperties = typeof(TMongoEntity).GetProperties().Where(
                prop => Attribute.IsDefined(prop, typeof(MongoUniqueStringIndex))).ToList();
            if (uniqueStringIndexProperties.Any())
                foreach (var propertyInfo in uniqueStringIndexProperties)
                {
                    var options = new CreateIndexOptions { Unique = true };
                    var propertyInfoName = propertyInfo.Name;
                    var field = new StringFieldDefinition<TMongoEntity>(propertyInfoName);
                    var indexDefinition = new IndexKeysDefinitionBuilder<TMongoEntity>().Ascending(field);
                    var indexModel = new CreateIndexModel<TMongoEntity>(indexDefinition, options);
                    _entityStore.Indexes.CreateOne(indexModel);
                }
        }
    
    0 讨论(0)
  • 2020-12-29 04:08

    The unique index only needs to be created once, after that any document inserts that contain a duplicate email address will fail. Here's an example:

    var server = MongoServer.Create("mongodb://localhost");
    var db = server.GetDatabase("myapp");
    
    var users = db.GetCollection<User>("users");
    
    users.EnsureIndex(new IndexKeysBuilder()
        .Ascending("EmailAddress"), IndexOptions.SetUnique(true));
    
    var user1 = new User { EmailAddress = "joe@example.com" };
    var user2 = new User { EmailAddress = "joe@example.com" };
    
    try
    {
        users.Save(user1, WriteConcern.Acknowledged);
        users.Save(user2, WriteConcern.Acknowledged);  // <-- throws MongoSafeModeException
    }
    catch (MongoSafeModeException ex)
    {
        Console.WriteLine(ex.Message);
    }
    
    0 讨论(0)
提交回复
热议问题