使用C#创建MongoDB唯一键

35

我正在尝试创建一个独特的字段EmailAddress,但是遇到了一些问题。 我已经在论坛上看到需要创建一个索引,但是目前为止我还没有成功。 有人有代码示例吗? 我需要在每次保存/调用时都创建索引,还是只需要创建一次就可以了?

我尝试了以下代码:

DB.GetCollection<User>(Dbname)
    .EnsureIndex(new IndexKeysBuilder()
        .Ascending("EmailAddress"), IndexOptions.SetUnique(true));

DB.GetCollection<User>(Dbname).Save(user, SafeMode.True);

我的 User 模型长这样:

public class User
{
    [Required(ErrorMessage = "Email Required")]
    public string EmailAddress { get; set; }

    public ObjectId Id { get; set; }

    public string FirstName { get; set; }
    public string LastName { get; set; }
}
9个回答

31

唯一索引只需要创建一次,之后包含重复电子邮件地址的任何文档插入操作都将失败。以下是一个示例:

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

28

根据C# mongo drivers 2.0规范,EnsureIndex()已被弃用/过时: http://api.mongodb.org/csharp/current/html/M_MongoDB_Driver_MongoCollection_EnsureIndex_2.htm

以下是如何使用异步和2.0代码进行操作:

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

如果索引不是字符串:

   var options = new CreateIndexOptions() { Unique = true };
   IndexKeysDefinition<Foo> keyCode = "{ Code: 1 }";
   var codeIndexModel = new CreateIndexModel<Foo>(keyCode, options);

10

截至2.8版本,下面是创建索引的方法,请注意最后两行。CreateOneAsync(indexDefinition, options)已经过时。

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

1
你可能想使用构建器,并通过表达式引用字段,而不是字符串,这样当重构(重命名属性)时,你的代码就会更加安全。请参见 https://dev59.com/bm025IYBdhLWcg3wGB0n#68756542 或其他答案以获取示例。 - Florian Winter

8
你的代码看起来没问题。这是一个完整的可运行程序,供你进行比较:
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
        }
    }
}

你可以使用mongo shell来确认索引是否已创建:
> 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
        }
]
>

它的运行很好,但我想要两个或更多字段来减少重复。请给我建议。 - Naveen

3

在我看来,实现这个的更好、更可行的方法是创建一个自定义属性,然后用c#获取带有该属性的属性并添加唯一索引。这种方法是通用的且非常容易使用。以下是示例:

我的自定义属性: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>();
}

然后你从哪里获取集合,例如在我的数据访问类中,我会这样做:
        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);
            }
    }

2

所有答案都缺少一个至关重要的点,那就是如果已经存在索引,则不要创建索引。

var db = _mongoClient.GetDatabase("db");
var coll = db.GetCollection<Coll>("collName");
var indexes = (await (await coll.Indexes.ListAsync()).ToListAsync()).Select(_ => _.GetElement("name").Value.ToString()).ToList();
// If index not present create it else skip.
if (indexes.Where(_ => _.Equals("<Index Name>")).Count() == 0)
{
    // Create Index here
}

1

这是一个使用MongoDB.Entities的工作程序(免责声明:我是作者)

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

尝试使用相同的电子邮件地址创建第二个用户会导致以下异常:
写操作导致错误。E11000重复键错误集合:test.User索引:EmailAddress(Asc) dup key: { : "email@domain.com" }

1

您需要将IndexKeysDefinition<TDocument>包装在CreateIndexModel<TDocument>中,以便能够指定选项,如Unique = true。示例:

collection.Indexes.CreateOne(
  new CreateIndexModel<DbModel>(
    Builders<DbModel>.IndexKeys.Ascending(d => d.SomeField),
    new CreateIndexOptions { Unique = true }
));

另请参阅https://dev59.com/bm025IYBdhLWcg3wGB0n#55210026


0

我正在使用<PackageReference Include="MongoDB.Driver" Version="2.11.5" />

以下是我的代码,用于创建唯一索引

var client = new MongoClient(mongourl);
var database = client.GetDatabase(dbName);

IMongoCollection<CollectionModel> myCollection = database.GetCollection<CollectionModel>("collection_name");

myCollection .Indexes.CreateOne(
      new CreateIndexModel<CollectionModel>(Builders<CollectionModel>.IndexKeys.Descending(model => model.YOUR_PROPERTY),
      new CreateIndexOptions { Unique = true }));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接