Ich finde das eine nette Idee, aber das muss man selbst machen, es gibt keine eingebaute Unterstützung dafür. Wenn Sie eine Zugriffsebene haben, können Sie es dort tun. Sie benötigen eine Attributklasse, etwa so;
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
Anschließend können Sie Attribute entweder auf Klassen- oder Mitgliedsebene wie folgt anwenden. Ich habe festgestellt, dass das Hinzufügen auf Mitgliedsebene im Vergleich zum Hinzufügen auf Klassenebene mit geringerer Wahrscheinlichkeit nicht mehr mit dem Schema synchron ist. Sie müssen natürlich sicherstellen, dass Sie den tatsächlichen Elementnamen und nicht den C#-Membernamen erhalten;
[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
}
und dann brauchen Sie in Ihrer DB-Zugriffsimplementierung (oder Ihrem Repository) so etwas;
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()
CertainIndexes sieht dann so aus:
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()
Beachten Sie, dass Sie dies nach jedem Update platzieren, da Ihre Indizes möglicherweise nicht erstellt werden, wenn Sie es irgendwo vergessen. Es ist daher wichtig sicherzustellen, dass Sie den Aufruf so optimieren, dass er schnell zurückgegeben wird, wenn keine Indizierung erforderlich ist, bevor Sie den gesamten Reflexionscode durchlaufen. Idealerweise tun Sie dies nur einmal oder zumindest einmal pro Anwendungsstart. Eine Möglichkeit wäre also, ein statisches Flag zu verwenden, um nachzuverfolgen, ob Sie dies bereits getan haben, und Sie würden einen zusätzlichen Sperrschutz dafür benötigen, aber vereinfacht gesagt sieht es in etwa so aus:
void AssureIndexes()
{
if (_requiresIndexing)
AssureIndexesInit();
}
Also das ist die Methode, die Sie in jedem DB-Update haben wollen, das Sie machen, das, wenn Sie Glück haben, auch vom JIT-Optimierer eingebunden wird.