mirror of
https://github.com/MichaelGrafnetter/DSInternals
synced 2024-12-12 17:37:31 +00:00
128 lines
4.5 KiB
C#
128 lines
4.5 KiB
C#
namespace DSInternals.DataStore
|
|
{
|
|
using Microsoft.Database.Isam;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
public class LinkResolver : IDisposable
|
|
{
|
|
// Column names:
|
|
private const string linkDNCol = "link_DNT";
|
|
private const string backlinkDNCol = "backlink_DNT";
|
|
private const string linkBaseCol = "link_base";
|
|
private const string linkDataCol = "link_data";
|
|
private const string linkMetadataCol = "link_metadata";
|
|
private const string linkDeletedTimeCol = "link_deltime";
|
|
private const string linkDeactivatedTimeCol = "link_deactivetime";
|
|
// Index of valid links post Recycle Bin feature
|
|
private const string linkIndex2008 = "link_present_active_index";
|
|
// Index of valid links before the Recycle Bin feature
|
|
private const string linkIndex2003 = "link_present_index";
|
|
|
|
private Cursor cursor;
|
|
private DirectorySchema schema;
|
|
|
|
public LinkResolver(IsamDatabase database, DirectorySchema schema)
|
|
{
|
|
this.schema = schema;
|
|
this.cursor = database.OpenCursor(ADConstants.LinkTableName);
|
|
if (this.cursor.TableDefinition.Indices.Contains(linkIndex2008))
|
|
{
|
|
this.cursor.SetCurrentIndex(linkIndex2008);
|
|
}
|
|
else
|
|
{
|
|
// Fallback to the old index if the newer one does not exist
|
|
cursor.SetCurrentIndex(linkIndex2003);
|
|
}
|
|
|
|
// TODO: Load column ids instead of names
|
|
}
|
|
|
|
public int? GetLinkedDNTag(int dnTag, string attributeName)
|
|
{
|
|
try
|
|
{
|
|
// Cast int to int? to force null as default value instead of 0
|
|
return this.GetLinkedDNTags(dnTag, attributeName).Cast<int?>().SingleOrDefault();
|
|
}
|
|
catch(InvalidOperationException)
|
|
{
|
|
// TODO: Make this a special exception type. Move message to resources.
|
|
throw new Exception("More than 1 objects have been found.");
|
|
}
|
|
}
|
|
|
|
public IEnumerable<int> GetLinkedDNTags(int dnTag, string attributeName)
|
|
{
|
|
if (!this.schema.ContainsAttribute(attributeName))
|
|
{
|
|
// If the schema does not contain this attribute at all, we pretend it to have an empty value.
|
|
yield break;
|
|
}
|
|
|
|
// TODO: Check that the attribute type is DN
|
|
this.FindLinkedRecords(dnTag, attributeName);
|
|
|
|
// The cursor is now before the first record in the link_table
|
|
while (cursor.MoveNext())
|
|
{
|
|
// TODO: Not deactivated?
|
|
int foundTag = (int)cursor.IndexRecord[backlinkDNCol];
|
|
yield return foundTag;
|
|
}
|
|
}
|
|
|
|
public IEnumerable<byte[]> GetLinkedValues(int dnTag, string attributeName)
|
|
{
|
|
if(!this.schema.ContainsAttribute(attributeName))
|
|
{
|
|
// If the schema does not contain this attribute at all, we pretend it to have an empty value.
|
|
yield break;
|
|
}
|
|
|
|
// TODO: Check that the attribute is DN-Binary.
|
|
this.FindLinkedRecords(dnTag, attributeName);
|
|
|
|
// The cursor is now before the first record in the link_table
|
|
while (cursor.MoveNext())
|
|
{
|
|
// TODO: Not deactivated?
|
|
byte[] foundValue = cursor.RetrieveColumnAsByteArray(linkDataCol);
|
|
yield return foundValue;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
this.Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (disposing && cursor != null)
|
|
{
|
|
cursor.Dispose();
|
|
cursor = null;
|
|
}
|
|
}
|
|
|
|
private void FindLinkedRecords(int dnTag, string attributeName)
|
|
{
|
|
SchemaAttribute attr = this.schema.FindAttribute(attributeName);
|
|
if (!attr.LinkId.HasValue)
|
|
{
|
|
//TODO: Throw a proper exception
|
|
throw new Exception("This is not a linked multivalue attribute.");
|
|
}
|
|
|
|
int linkBase = attr.LinkBase.Value;
|
|
// Columns order in index: link_DNT, link_base, backlink_DNT
|
|
Key key = Key.Compose(dnTag, linkBase);
|
|
key.AddWildcard();
|
|
this.cursor.FindRecords(MatchCriteria.EqualTo, key);
|
|
}
|
|
}
|
|
} |