Как украсить элемент класса, чтобы он стал индексом и получал то же самое, что и в случае с sureIndex? - PullRequest
3 голосов
/ 19 октября 2011

Я хотел бы определить в объявлении класса, какие элементы являются индексными, что-то вроде:

public class MyClass {
    public int SomeNum { get; set; }
    [THISISANINDEX]
    public string SomeProperty { get; set; }
}

, чтобы иметь такой же эффект, как sureIndex ("SomeProperty")

возможно

Ответы [ 2 ]

7 голосов
/ 16 июля 2012

Я думаю, что это хорошая идея, но вы должны сделать это самостоятельно, для нее нет встроенной поддержки. Если у вас есть слой доступа, вы можете сделать это там. Вам понадобится класс атрибута, что-то вроде этого;

public enum IndexConstraints
{
    Normal     = 0x00000001, // Ascending, non-indexed
    Descending = 0x00000010,
    Unique     = 0x00000100,
    Sparse     = 0x00001000, // allows nulls in the indexed fields
}

// Applied to a member
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class EnsureIndexAttribute : EnsureIndexes
{
    public EnsureIndex(IndexConstraints ic = IndexConstraints.Normal) : base(ic) { }
}

// Applied to a class
[AttributeUsage(AttributeTargets.Class)]
public class EnsureIndexesAttribute : Attribute
{
    public bool Descending { get; private set; }
    public bool Unique { get; private set; }
    public bool Sparse { get; private set; }
    public string[] Keys { get; private set; }

    public EnsureIndexes(params string[] keys) : this(IndexConstraints.Normal, keys) {}
    public EnsureIndexes(IndexConstraints ic, params string[] keys)
    {
        this.Descending = ((ic & IndexConstraints.Descending) != 0);
        this.Unique = ((ic & IndexConstraints.Unique) != 0); ;
        this.Sparse = ((ic & IndexConstraints.Sparse) != 0); ;
        this.Keys = keys;
    }

}//class EnsureIndexes

Затем вы можете применять атрибуты на уровне класса или члена следующим образом. Я обнаружил, что добавление на уровне члена с меньшей вероятностью не синхронизируется со схемой по сравнению с добавлением на уровне класса. Конечно же, вам нужно убедиться, что вы получаете фактическое имя элемента, а не имя члена C #;

[CollectionName("People")]
//[EnsureIndexes("k")]// doing it here would allow for multi-key configs
public class Person 
{
    [BsonElement("k")] // name mapping in the DB schema
    [BsonIgnoreIfNull]
    [EnsureIndex(IndexConstraints.Unique|IndexConstraints.Sparse)] // name is implicit here
    public string userId{ get; protected set; }

// other properties go here
}

и затем в вашей реализации доступа к БД (или хранилище) вам нужно что-то вроде этого;

    private void AssureIndexesNotInlinable()
    {
                // We can only index a collection if there's at least one element, otherwise it does nothing
                if (this.collection.Count() > 0)
                {

                    // Check for EnsureIndex Attribute
                    var theClass = typeof(T);

                    // Walk the members of the class to see if there are any directly attached index directives
                    foreach (var m in theClass.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
                    {
                        List<string> elementNameOverride = new List<string>(1);
                        EnsureIndexes indexAttr = null;

                        // For each members attribs
                        foreach (Attribute attr in m.GetCustomAttributes())
                        {
                            if (attr.GetType() == typeof(EnsureIndex))
                                indexAttr = (EnsureIndex)attr;

                            if (attr.GetType() == typeof(RepoElementAttribute))
                                elementNameOverride.Add(((RepoElementAttribute)attr).ElementName);

                            if ((indexAttr != null) && (elementNameOverride.Count != 0))
                                break;
                        }

                        // Index
                        if (indexAttr != null)
                        {
                            if (elementNameOverride.Count() > 0)
                                EnsureIndexesAsDeclared(indexAttr, elementNameOverride);
                            else
                                EnsureIndexesAsDeclared(indexAttr);
                        }
                    }

                    // Walk the atributes on the class itself. WARNING: We don't validate the member names here, we just create the indexes
                    // so if you create a unique index and don't have a field to match you'll get an exception as you try to add the second
                    // item with a null value on that key
                    foreach (Attribute attr in theClass.GetCustomAttributes(true))
                    {
                        if (attr.GetType() == typeof(EnsureIndexes))
                            EnsureIndexesAsDeclared((EnsureIndexes)attr);

                    }//foreach

                }//if this.collection.count

    }//AssureIndexesNotInlinable()

Тогда EnsureIndexes выглядит так:

    private void EnsureIndexesAsDeclared(EnsureIndexes attr, List<string> indexFields = null)
    {
        var eia = attr as EnsureIndexes;

        if (indexFields == null)
            indexFields = eia.Keys.ToList();

        // use driver specific methods to actually create this index on the collection
        var db = GetRepositoryManager(); // if you have a repository or some other method of your own 
        db.EnsureIndexes(indexFields, attr.Descending, attr.Unique, attr.Sparse);

    }//EnsureIndexes()

Обратите внимание, что вы разместите это после каждого обновления, потому что если вы где-то забудете, ваши индексы могут не быть созданы. Поэтому важно убедиться, что вы оптимизируете вызов так, чтобы он быстро возвращался, если не нужно выполнять индексацию, прежде чем пройти через весь этот код отражения. В идеале вы должны делать это только один раз или, по крайней мере, один раз за запуск приложения. Так что одним из способов было бы использовать статический флаг для отслеживания того, сделали ли вы это, и вам понадобится дополнительная защита от блокировки, но слишком упрощенно это выглядит примерно так:

    void AssureIndexes()
    {
        if (_requiresIndexing)
            AssureIndexesInit();
    }

Так что это метод, который вы захотите при каждом обновлении БД, которое вы делаете, что, если вам повезет, также будет встроено оптимизатором JIT.

2 голосов
/ 22 октября 2011

См. Ниже наивную реализацию, которая может быть полезна для некоторых мозгов, чтобы принять во внимание рекомендацию по индексированию из документации MongoDb. Создание индексов на основе запросов, используемых в приложении, вместо добавления пользовательских атрибутов к свойствам может быть другой возможностью.

using System;
using System.Reflection;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using NUnit.Framework;
using SharpTestsEx;

namespace Mongeek
{
    [TestFixture]
    class TestDecorateToEnsureIndex
    {
        [Test]
        public void ShouldIndexPropertyWithEnsureIndexAttribute() 
        {
            var server = MongoServer.Create("mongodb://localhost");
            var db = server.GetDatabase("IndexTest");
            var boatCollection = db.GetCollection<Boat>("Boats");
            boatCollection.DropAllIndexes();

            var indexer = new Indexer();
            indexer.EnsureThat(boatCollection).HasIndexesNeededBy<Boat>();

            boatCollection.IndexExists(new[] { "Name" }).Should().Be.True();
        }
    }

    internal class Indexer
    {
        private MongoCollection _mongoCollection;

        public Indexer EnsureThat(MongoCollection mongoCollection)
        {
            _mongoCollection = mongoCollection;
            return this;
        }

        public Indexer HasIndexesNeededBy<T>()
        {
            Type t = typeof (T);
            foreach(PropertyInfo prop in t.GetProperties() )
            {
                if (Attribute.IsDefined(prop, typeof (EnsureIndexAttribute)))
                {
                    _mongoCollection.EnsureIndex(new[] {prop.Name});
                }
            }
            return this;
        }
    }

    internal class Boat
    {
        public Boat(Guid id)
        {
            Id = id;
        }

        [BsonId]
        public Guid Id { get; private set; }

        public int Length { get; set; }

        [EnsureIndex]
        public string Name { get; set; }
    }

    internal class EnsureIndexAttribute  : Attribute
    {
    }
}
...