├── linqtoldap_logo.png ├── LinqToLdap ├── linqtoldap.snk ├── Collections │ ├── Nothing.cs │ ├── VirtualListView.cs │ ├── IVirtualListView.cs │ ├── ILdapPage.cs │ ├── SerializableKeyValuePair.cs │ ├── OriginalValuesCollection.cs │ ├── SafeList.cs │ ├── LdapPage.cs │ └── SearchResponseEnumerable.cs ├── QueryCommands │ ├── IQueryCommandFactory.cs │ ├── Options │ │ ├── PagingOptions.cs │ │ ├── ISortingOptions.cs │ │ ├── IPagingOptions.cs │ │ ├── ListAttributesQueryCommandOptions.cs │ │ ├── ProjectionQueryCommandOptions.cs │ │ ├── StandardQueryCommandOptions.cs │ │ ├── SortingOptions.cs │ │ ├── DynamicQueryCommandOptions.cs │ │ └── QueryCommandOptions.cs │ ├── QueryCommandType.cs │ ├── QueryCommandFactory.cs │ ├── GetRequestCommand.cs │ ├── IQueryCommand.cs │ ├── QueryCommand.cs │ └── AnyQueryCommand.cs ├── EventListeners │ ├── IPreAddEventListener.cs │ ├── IPreDeleteEventListener.cs │ ├── IPreUpdateEventListener.cs │ ├── IPostAddEventListener.cs │ ├── IPostDeleteEventListener.cs │ ├── IPostUpdateEventListener.cs │ ├── IEventListener.cs │ ├── ListenerPreArgs.cs │ └── ListenerPostArgs.cs ├── EnumerableExtensions.cs ├── IAsyncQueryProvider.cs ├── IPooledLdapConnectionFactory.cs ├── Exceptions │ ├── FilterException.cs │ ├── MappingException.cs │ └── MissingConstructorException.cs ├── OC.cs ├── Visitors │ ├── EnumerableVisitor.cs │ ├── NullVisitor.cs │ ├── MemberVisitor.cs │ ├── DynamicSelectProjector.cs │ ├── SelectProjector.cs │ ├── Nominator.cs │ └── BooleanReducer.cs ├── ILdapConnectionFactory.cs ├── Transformers │ ├── IResultTransformer.cs │ ├── RawEntryResultTransformer.cs │ ├── DynamicResultTransformer.cs │ └── ProjectionResultTransformer.cs ├── Mapping │ ├── PropertyMappings │ │ ├── PropertyMappingArguments.cs │ │ ├── StringPropertyMapping.cs │ │ ├── CatchAllPropertyMapping.cs │ │ ├── NumericPropertyMapping.cs │ │ ├── BooleanPropertyMapping.cs │ │ ├── GuidPropertyMapping.cs │ │ ├── X509Certificate2PropertyMapping.cs │ │ ├── SecurityIdentifierPropertyMapping.cs │ │ ├── EnumPropertyMapping.cs │ │ ├── ByteArrayPropertyMapping.cs │ │ ├── StringArrayPropertyMapping.cs │ │ ├── ByteArrayArrayPropertyMapping.cs │ │ ├── StringCollectionPropertyMapping.cs │ │ ├── ByteArrayCollectionPropertyMapping.cs │ │ └── DatePropertyMapping.cs │ ├── ReadOnly.cs │ ├── IDirectoryObject.cs │ ├── DistinguishedNameAttribute.cs │ ├── IPropertyMappingBuilder.cs │ ├── AnonymousObjectMapping.cs │ ├── IClassMap.cs │ ├── DirectoryObjectBase.cs │ ├── DirectorySchemaAttribute.cs │ ├── PropertyMappingBuilders │ │ ├── IDateTimePropertyMappingBuilder.cs │ │ └── IDirectoryValueConversionMapper.cs │ ├── SubClassMap.cs │ ├── StandardObjectMapping.cs │ ├── ServerObjectMapping.cs │ ├── PropertyMappingGeneric.cs │ ├── PropertyMapping.cs │ ├── ICustomPropertyMapper.cs │ └── AutoClassMap.cs ├── SelectProjection.cs ├── Helpers │ ├── InternalTuple.cs │ ├── TypeSystem.cs │ └── ObjectActivator.cs ├── Logging │ ├── ILinqToLdapLogger.cs │ └── TraceTextWriter.cs ├── ExposeInternals.cs ├── QueryProvider.cs ├── LinqToLdap.csproj ├── ILdapConfiguration.cs ├── DirectoryQuery.cs └── ILdapConnectionFactoryConfiguration.cs ├── linqtoldap_logo_small.png ├── LinqToLdap.Tests ├── Resources │ ├── reddit.png │ ├── cert.cer │ └── cert2.cer ├── linqtoldap_tests.snk ├── QueryCommands │ ├── ListEntriesQueryCommand.cs │ └── GetRequestCommandTest.cs ├── Extensions │ ├── CleanFilterValueTest.cs │ └── TypeExtensionsTest.cs ├── TestSupport │ ├── DirectoryControlFactory.cs │ ├── MockQueryContext.cs │ ├── ExtensionMethods │ │ └── TypeExtensions.cs │ ├── MockQueryCommandFactory.cs │ ├── MockLdapConnection.cs │ └── QueryCommands │ │ └── MockStandardQueryCommand.cs ├── QueryTranslatorTestClass.cs ├── LinqToLdap.Tests.csproj ├── Helpers │ └── ObjectActivatorTests.cs ├── Collections │ └── SearchResponseEnumeratorIntegrationTest.cs └── Mapping │ ├── SubClassMapTest.cs │ └── LdapConfigurationTest.cs ├── LinqToLdap.NET35.Tests ├── Resources │ ├── reddit.png │ ├── cert.cer │ └── cert2.cer ├── packages.config └── Properties │ └── AssemblyInfo.cs ├── LinqToLdap.NET40.Tests ├── Resources │ ├── reddit.png │ ├── cert.cer │ └── cert2.cer ├── packages.config └── Properties │ └── AssemblyInfo.cs ├── LinqToLdap.NET45.Tests ├── Resources │ ├── reddit.png │ ├── cert.cer │ └── cert2.cer ├── packages.config ├── Properties │ └── AssemblyInfo.cs └── app.config ├── LinqToLdap.Tests.PopulateDirectory ├── LinqToLdap.Tests.PopulateDirectory.csproj └── LdsUser.cs ├── LinqToLdap.Tests.ClassMapAssembly ├── LinqToLdap.Tests.ClassMapAssembly.csproj └── AssemblyTestClass.cs └── LICENSE /linqtoldap_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madhatter22/LinqToLdap/HEAD/linqtoldap_logo.png -------------------------------------------------------------------------------- /LinqToLdap/linqtoldap.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madhatter22/LinqToLdap/HEAD/LinqToLdap/linqtoldap.snk -------------------------------------------------------------------------------- /linqtoldap_logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madhatter22/LinqToLdap/HEAD/linqtoldap_logo_small.png -------------------------------------------------------------------------------- /LinqToLdap.Tests/Resources/reddit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madhatter22/LinqToLdap/HEAD/LinqToLdap.Tests/Resources/reddit.png -------------------------------------------------------------------------------- /LinqToLdap.Tests/linqtoldap_tests.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madhatter22/LinqToLdap/HEAD/LinqToLdap.Tests/linqtoldap_tests.snk -------------------------------------------------------------------------------- /LinqToLdap.NET35.Tests/Resources/reddit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madhatter22/LinqToLdap/HEAD/LinqToLdap.NET35.Tests/Resources/reddit.png -------------------------------------------------------------------------------- /LinqToLdap.NET40.Tests/Resources/reddit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madhatter22/LinqToLdap/HEAD/LinqToLdap.NET40.Tests/Resources/reddit.png -------------------------------------------------------------------------------- /LinqToLdap.NET45.Tests/Resources/reddit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madhatter22/LinqToLdap/HEAD/LinqToLdap.NET45.Tests/Resources/reddit.png -------------------------------------------------------------------------------- /LinqToLdap/Collections/Nothing.cs: -------------------------------------------------------------------------------- 1 | namespace LinqToLdap.Collections 2 | { 3 | internal sealed class Nothing 4 | { 5 | public static readonly Nothing Value = new Nothing(); 6 | private Nothing() { } 7 | } 8 | } -------------------------------------------------------------------------------- /LinqToLdap.NET35.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /LinqToLdap.NET40.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/IQueryCommandFactory.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Mapping; 2 | using LinqToLdap.QueryCommands.Options; 3 | 4 | namespace LinqToLdap.QueryCommands 5 | { 6 | internal interface IQueryCommandFactory 7 | { 8 | IQueryCommand GetCommand(QueryCommandType type, IQueryCommandOptions options, IObjectMapping mapping); 9 | } 10 | } -------------------------------------------------------------------------------- /LinqToLdap/EventListeners/IPreAddEventListener.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace LinqToLdap.EventListeners 4 | { 5 | /// 6 | /// The event raised before an add occurs. 7 | /// 8 | public interface IPreAddEventListener : IPreEventListener, IAddEventListener 9 | { 10 | 11 | } 12 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests.PopulateDirectory/LinqToLdap.Tests.PopulateDirectory.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LinqToLdap/EventListeners/IPreDeleteEventListener.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace LinqToLdap.EventListeners 4 | { 5 | /// 6 | /// The event raised before a delete occurs. 7 | /// 8 | public interface IPreDeleteEventListener : IPreEventListener, IDeleteEventListener 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /LinqToLdap/EventListeners/IPreUpdateEventListener.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace LinqToLdap.EventListeners 4 | { 5 | /// 6 | /// The event raised before an update occurs. 7 | /// 8 | public interface IPreUpdateEventListener : IPreEventListener, IUpdateEventListener 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /LinqToLdap/EventListeners/IPostAddEventListener.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace LinqToLdap.EventListeners 4 | { 5 | /// 6 | /// The event raised after an add occurs. 7 | /// 8 | public interface IPostAddEventListener : IPostEventListener, IAddEventListener 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /LinqToLdap/EventListeners/IPostDeleteEventListener.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace LinqToLdap.EventListeners 4 | { 5 | /// 6 | /// The event raised after a delete occurs. 7 | /// 8 | public interface IPostDeleteEventListener : IPostEventListener, IDeleteEventListener 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /LinqToLdap/EventListeners/IPostUpdateEventListener.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace LinqToLdap.EventListeners 4 | { 5 | /// 6 | /// The event raised after an update occurs. 7 | /// 8 | public interface IPostUpdateEventListener : IPostEventListener, IUpdateEventListener 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /LinqToLdap/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace LinqToLdap 5 | { 6 | internal static class EnumerableExtensions 7 | { 8 | public static void ForEach(this IEnumerable enumerable, Action action) 9 | { 10 | foreach (var item in enumerable) 11 | { 12 | action(item); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /LinqToLdap/IAsyncQueryProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Linq.Expressions; 3 | 4 | namespace LinqToLdap 5 | { 6 | internal interface IAsyncQueryProvider : IQueryProvider 7 | { 8 | #if (!NET35 && !NET40) 9 | 10 | System.Threading.Tasks.Task ExecuteAsync(Expression expression); 11 | 12 | System.Threading.Tasks.Task ExecuteAsync(Expression expression); 13 | 14 | #endif 15 | } 16 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/Options/PagingOptions.cs: -------------------------------------------------------------------------------- 1 | namespace LinqToLdap.QueryCommands.Options 2 | { 3 | internal class PagingOptions : IPagingOptions 4 | { 5 | public PagingOptions(int pageSize, byte[] nextPage) 6 | { 7 | NextPage = nextPage; 8 | PageSize = pageSize; 9 | } 10 | 11 | public byte[] NextPage { get; private set; } 12 | public int PageSize { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/Options/ISortingOptions.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace LinqToLdap.QueryCommands.Options 4 | { 5 | /// 6 | /// Interface the contains s for a query. 7 | /// 8 | public interface ISortingOptions 9 | { 10 | /// 11 | /// The keys containing sort information. 12 | /// 13 | SortKey[] Keys { get; } 14 | } 15 | } -------------------------------------------------------------------------------- /LinqToLdap/IPooledLdapConnectionFactory.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace LinqToLdap 4 | { 5 | /// 6 | /// Interface for constructing s and managing them in a pool. 7 | /// 8 | public interface IPooledLdapConnectionFactory : ILdapConnectionFactory 9 | { 10 | /// 11 | /// Reinitializes the pool. 12 | /// 13 | void Reinitialize(); 14 | } 15 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/Options/IPagingOptions.cs: -------------------------------------------------------------------------------- 1 | namespace LinqToLdap.QueryCommands.Options 2 | { 3 | /// 4 | /// Interface for paging options from a Query. 5 | /// 6 | public interface IPagingOptions 7 | { 8 | /// 9 | /// The next page. 10 | /// 11 | byte[] NextPage { get; } 12 | 13 | /// 14 | /// The size of the page. 15 | /// 16 | int PageSize { get; } 17 | } 18 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/QueryCommands/ListEntriesQueryCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | 7 | namespace LinqToLdap.Tests.QueryCommands 8 | { 9 | [TestClass] 10 | public class ListEntriesQueryCommand 11 | { 12 | [TestMethod] 13 | public void Execute_SomeEntries_ReturnsSearchResponseEnumeratorWithRawResult() 14 | { 15 | 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LinqToLdap.Tests/Extensions/CleanFilterValueTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SharpTestsEx; 3 | 4 | namespace LinqToLdap.Tests.Extensions 5 | { 6 | [TestClass] 7 | public class CleanFilterValueTest 8 | { 9 | [TestMethod] 10 | public void CleanFilterValue() 11 | { 12 | string value = "!&:|~a\\*()\u0000z"; 13 | 14 | value.CleanFilterValue().Should().Be.EqualTo("\\21\\26\\3a\\7c\\7ea\\5c\\2a\\28\\29\\00z"); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LinqToLdap/Exceptions/FilterException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LinqToLdap.Exceptions 4 | { 5 | /// 6 | /// Exception to indicate that something went wrong with filter creation. 7 | /// 8 | public class FilterException : Exception 9 | { 10 | internal FilterException(string message) : base(message) 11 | { 12 | } 13 | 14 | internal FilterException(string message, Exception innerException) 15 | : base(message, innerException) 16 | { 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /LinqToLdap/Exceptions/MappingException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LinqToLdap.Exceptions 4 | { 5 | /// 6 | /// Exception to indicate that something has been mapped incorrectly or not at all. 7 | /// 8 | public class MappingException : Exception 9 | { 10 | internal MappingException(string message) : base(message) 11 | { 12 | } 13 | 14 | internal MappingException(string message, Exception innerException) : base(message, innerException) 15 | { 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /LinqToLdap/Exceptions/MissingConstructorException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LinqToLdap.Exceptions 4 | { 5 | /// 6 | /// Exception thrown if delegate creation fails for mapped objects 7 | /// 8 | public class MissingConstructorException : Exception 9 | { 10 | internal MissingConstructorException(string message) : base(message) 11 | { 12 | } 13 | 14 | internal MissingConstructorException(string message, Exception innerException) 15 | : base(message, innerException) 16 | { 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /LinqToLdap/OC.cs: -------------------------------------------------------------------------------- 1 | namespace LinqToLdap 2 | { 3 | /// 4 | /// Enum for indicating Object Classes and or Object Category to be included / excluded from queries. 5 | /// 6 | public enum OC 7 | { 8 | /// 9 | /// Only Object Category 10 | /// 11 | ObjectCategory, 12 | /// 13 | /// Only Object Classes 14 | /// 15 | ObjectClasses, 16 | /// 17 | /// Object Category and Object Classes 18 | /// 19 | Both 20 | } 21 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/TestSupport/DirectoryControlFactory.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | using LinqToLdap.Tests.TestSupport.ExtensionMethods; 3 | 4 | namespace LinqToLdap.Tests.TestSupport 5 | { 6 | public class DirectoryControlFactory 7 | { 8 | public static PageResultResponseControl CreatePageResponse(byte[] cookie, byte[] controlValue, int count = 0, bool criticality = true) 9 | { 10 | return typeof (PageResultResponseControl) 11 | .Create(new object[] { count, cookie, criticality, controlValue }); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /LinqToLdap/Visitors/EnumerableVisitor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Linq.Expressions; 3 | 4 | namespace LinqToLdap.Visitors 5 | { 6 | internal class EnumerableVisitor : ExpressionVisitor 7 | { 8 | private IEnumerable _list; 9 | 10 | public IEnumerable GetList(Expression expression) 11 | { 12 | Visit(expression); 13 | return _list; 14 | } 15 | 16 | protected override Expression VisitConstant(ConstantExpression c) 17 | { 18 | _list = c.Value as IEnumerable; 19 | return base.VisitConstant(c); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/QueryTranslatorTestClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LinqToLdap.Tests 4 | { 5 | public enum QueryTranslatorEnum 6 | { 7 | Value = 0, 8 | Value2 = 1 9 | } 10 | 11 | public class QueryTranslatorTestClass 12 | { 13 | public string Property1 { get; set; } 14 | public string Property2 { get; set; } 15 | public int Property3 { get; set; } 16 | public DateTime? Property4 { get; set; } 17 | public bool Property5 { get; set; } 18 | public Guid Property6 { get; set; } 19 | public QueryTranslatorEnum Property7 { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /LinqToLdap.NET45.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /LinqToLdap/Visitors/NullVisitor.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | 3 | namespace LinqToLdap.Visitors 4 | { 5 | internal class NullVisitor : ExpressionVisitor 6 | { 7 | private bool _isNull; 8 | 9 | public NullVisitor() 10 | { 11 | _isNull = false; 12 | } 13 | 14 | public bool IsNull(Expression expression) 15 | { 16 | Visit(expression); 17 | return _isNull; 18 | } 19 | 20 | protected override Expression VisitConstant(ConstantExpression c) 21 | { 22 | _isNull = c.Value == null || string.Empty.Equals(c.Value); 23 | return c; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /LinqToLdap/Collections/VirtualListView.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace LinqToLdap.Collections 4 | { 5 | internal class VirtualListView : List, IVirtualListView 6 | { 7 | public VirtualListView(int contentCount, byte[] contextId, int targetPosition, IEnumerable view) 8 | : base(view) 9 | { 10 | ContentCount = contentCount; 11 | ContextId = contextId; 12 | TargetPosition = targetPosition; 13 | } 14 | 15 | public int ContentCount { get; private set; } 16 | public byte[] ContextId { get; private set; } 17 | public int TargetPosition { get; private set; } 18 | } 19 | } -------------------------------------------------------------------------------- /LinqToLdap/Visitors/MemberVisitor.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | 3 | namespace LinqToLdap.Visitors 4 | { 5 | internal class MemberVisitor : ExpressionVisitor 6 | { 7 | private Expression _member; 8 | 9 | public Expression GetMember(Expression expression) 10 | { 11 | Visit(expression); 12 | return _member; 13 | } 14 | 15 | protected override Expression VisitMemberAccess(MemberExpression m) 16 | { 17 | if (m != null && m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter) 18 | { 19 | _member = m; 20 | } 21 | return m; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests.ClassMapAssembly/LinqToLdap.Tests.ClassMapAssembly.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1;netstandard2.0;net35;net40;net45 5 | true 6 | false 7 | C:\Users\alanh\Code\local\LinqToLdap\LinqToLdap.Tests\linqtoldap_tests.snk 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /LinqToLdap/ILdapConnectionFactory.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace LinqToLdap 4 | { 5 | /// 6 | /// Interface for constructing s 7 | /// 8 | public interface ILdapConnectionFactory 9 | { 10 | /// 11 | /// Builds a based on a fluent configuration. 12 | /// 13 | /// 14 | LdapConnection GetConnection(); 15 | 16 | /// 17 | /// Releases a . 18 | /// 19 | /// 20 | void ReleaseConnection(LdapConnection connection); 21 | } 22 | } -------------------------------------------------------------------------------- /LinqToLdap/Transformers/IResultTransformer.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace LinqToLdap.Transformers 4 | { 5 | /// 6 | /// Interface for transforming query results 7 | /// 8 | public interface IResultTransformer 9 | { 10 | /// 11 | /// Transform the 12 | /// 13 | /// The entry to transform 14 | /// 15 | object Transform(SearchResultEntry entry); 16 | 17 | /// 18 | /// Get the default value of the transform. 19 | /// 20 | /// 21 | object Default(); 22 | } 23 | } -------------------------------------------------------------------------------- /LinqToLdap/Transformers/RawEntryResultTransformer.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Collections; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | 5 | namespace LinqToLdap.Transformers 6 | { 7 | internal class RawEntryResultTransformer : IResultTransformer 8 | { 9 | public object Transform(SearchResultEntry entry) 10 | { 11 | return new KeyValuePair>>( 12 | entry.DistinguishedName, 13 | new DirectoryAttributes(entry)); 14 | } 15 | 16 | public object Default() 17 | { 18 | return default(KeyValuePair>>); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /LinqToLdap.NET45.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("LinqToLdap.NET45.Tests")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("LinqToLdap.NET45.Tests")] 10 | [assembly: AssemblyCopyright("Copyright © 2019")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("1effced2-7f5c-4059-93a7-533887d0e2d9")] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion("1.0.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0.0")] 21 | -------------------------------------------------------------------------------- /LinqToLdap.NET45.Tests/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/PropertyMappingArguments.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace LinqToLdap.Mapping.PropertyMappings 5 | { 6 | internal class PropertyMappingArguments 7 | { 8 | public string PropertyName { get; set; } 9 | public Type PropertyType { get; set; } 10 | public string AttributeName { get; set; } 11 | public Func Getter { get; set; } 12 | public Action Setter { get; set; } 13 | public bool IsDistinguishedName { get; set; } 14 | public ReadOnly ReadOnly { get; set; } 15 | public Dictionary DirectoryMappings { get; set; } 16 | public Dictionary InstanceMappings { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/ReadOnly.cs: -------------------------------------------------------------------------------- 1 | namespace LinqToLdap.Mapping 2 | { 3 | /// 4 | /// Controls the read only strategy for a property. 5 | /// 6 | public enum ReadOnly 7 | { 8 | /// 9 | /// Never read only 10 | /// 11 | Never = 0, 12 | 13 | /// 14 | /// Read only during add operations but will be considered during update operations. 15 | /// 16 | OnAdd = 1, 17 | 18 | /// 19 | /// Read only during update operations but will be considered during add operations. 20 | /// 21 | OnUpdate = 2, 22 | 23 | /// 24 | /// Always read only 25 | /// 26 | Always = 3 27 | } 28 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/TestSupport/MockQueryContext.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Collections; 2 | using LinqToLdap.TestSupport; 3 | 4 | namespace LinqToLdap.Tests.TestSupport 5 | { 6 | public class MockQueryContext 7 | { 8 | public MockQueryProvider ActiveProvider { get; private set; } 9 | 10 | public MockQuery Query() 11 | { 12 | ActiveProvider = new MockQueryProvider(null); 13 | var query = new MockQuery(ActiveProvider); 14 | 15 | return query; 16 | } 17 | 18 | public MockQuery Query() 19 | { 20 | ActiveProvider = new MockQueryProvider(null); 21 | var query = new MockQuery(ActiveProvider); 22 | 23 | return query; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LinqToLdap/SelectProjection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | 5 | namespace LinqToLdap 6 | { 7 | internal class SelectProjection 8 | { 9 | public SelectProjection(IDictionary selectedProperties, LambdaExpression projection) 10 | { 11 | SelectedProperties = selectedProperties; 12 | 13 | Projection = projection.Compile(); 14 | 15 | #if NET35 16 | ReturnType = projection.Body.Type; 17 | #else 18 | ReturnType = projection.ReturnType; 19 | #endif 20 | } 21 | 22 | public IDictionary SelectedProperties { get; private set; } 23 | public Delegate Projection { get; private set; } 24 | public Type ReturnType { get; private set; } 25 | } 26 | } -------------------------------------------------------------------------------- /LinqToLdap/Helpers/InternalTuple.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LinqToLdap.Helpers 6 | { 7 | internal class ThreeTuple 8 | { 9 | public ThreeTuple(T1 t1, T2 t2, T3 t3) 10 | { 11 | Item1 = t1; 12 | Item2 = t2; 13 | Item3 = t3; 14 | } 15 | 16 | public T1 Item1 { get; private set; } 17 | public T2 Item2 { get; private set; } 18 | public T3 Item3 { get; private set; } 19 | } 20 | 21 | internal class TwoTuple 22 | { 23 | public TwoTuple(T1 t1, T2 t2) 24 | { 25 | Item1 = t1; 26 | Item2 = t2; 27 | } 28 | 29 | public T1 Item1 { get; private set; } 30 | public T2 Item2 { get; private set; } 31 | } 32 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/Options/ListAttributesQueryCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Transformers; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace LinqToLdap.QueryCommands.Options 6 | { 7 | internal class ListAttributesQueryCommandOptions : QueryCommandOptions 8 | { 9 | public ListAttributesQueryCommandOptions(Dictionary attributes) 10 | : base(attributes ?? new Dictionary()) 11 | { 12 | } 13 | 14 | public override Type GetEnumeratorReturnType() 15 | { 16 | return typeof(KeyValuePair>>); 17 | } 18 | 19 | public override IResultTransformer GetTransformer() 20 | { 21 | return new RawEntryResultTransformer(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/IDirectoryObject.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Collections; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | 5 | namespace LinqToLdap.Mapping 6 | { 7 | /// 8 | /// Interface for updatable directory objects. 9 | /// 10 | public interface IDirectoryObject 11 | { 12 | /// 13 | /// Gets the changes to send to the directory. 14 | /// 15 | /// The mapping for the object. 16 | /// 17 | IEnumerable GetChanges(IObjectMapping mapping); 18 | 19 | /// 20 | /// The original property values loaded form the directory for this object. 21 | /// 22 | OriginalValuesCollection OriginalValues { get; set; } 23 | } 24 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/Options/ProjectionQueryCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Mapping; 2 | using LinqToLdap.Transformers; 3 | using System; 4 | 5 | namespace LinqToLdap.QueryCommands.Options 6 | { 7 | internal class ProjectionQueryCommandOptions : QueryCommandOptions 8 | { 9 | private readonly IObjectMapping _mapping; 10 | 11 | public ProjectionQueryCommandOptions(IObjectMapping mapping, SelectProjection selectProjection) : base(selectProjection) 12 | { 13 | _mapping = mapping; 14 | } 15 | 16 | public override Type GetEnumeratorReturnType() 17 | { 18 | return SelectProjection.ReturnType; 19 | } 20 | 21 | public override IResultTransformer GetTransformer() 22 | { 23 | return new ProjectionResultTransformer(SelectProjection, AttributesToLoad, _mapping); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/Options/StandardQueryCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Mapping; 2 | using LinqToLdap.Transformers; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace LinqToLdap.QueryCommands.Options 7 | { 8 | internal class StandardQueryCommandOptions : QueryCommandOptions 9 | { 10 | private readonly IObjectMapping _mapping; 11 | 12 | public StandardQueryCommandOptions(IObjectMapping mapping, IDictionary queriedAttributes) 13 | : base(queriedAttributes) 14 | { 15 | _mapping = mapping; 16 | } 17 | 18 | public override Type GetEnumeratorReturnType() 19 | { 20 | return _mapping.Type; 21 | } 22 | 23 | public override IResultTransformer GetTransformer() 24 | { 25 | return new ResultTransformer(AttributesToLoad, _mapping); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/DistinguishedNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LinqToLdap.Mapping 4 | { 5 | /// 6 | /// Indicates that the property will contain the distinguished name for a class. 7 | /// 8 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] 9 | public class DistinguishedNameAttribute : Attribute 10 | { 11 | /// 12 | /// Maps a property to the specified . 13 | /// 14 | /// Name of the attribute. 15 | public DistinguishedNameAttribute(string attributeName = "distinguishedname") 16 | { 17 | AttributeName = attributeName; 18 | } 19 | 20 | /// 21 | /// The mapped attribute name. 22 | /// 23 | public string AttributeName { get; private set; } 24 | } 25 | } -------------------------------------------------------------------------------- /LinqToLdap/Collections/IVirtualListView.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace LinqToLdap.Collections 4 | { 5 | /// 6 | /// Interface for a collection of items in a view. 7 | /// 8 | /// 9 | public interface IVirtualListView : IEnumerable 10 | { 11 | /// 12 | /// The number of items in this section of the view. 13 | /// 14 | int Count { get; } 15 | 16 | /// 17 | /// The total number of items in the view. 18 | /// 19 | int ContentCount { get; } 20 | 21 | /// 22 | /// The context ID assigned by the server to identify this search. 23 | /// 24 | byte[] ContextId { get; } 25 | 26 | /// 27 | /// The list index position of the target entry. 28 | /// 29 | int TargetPosition { get; } 30 | } 31 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/TestSupport/ExtensionMethods/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | 5 | namespace LinqToLdap.Tests.TestSupport.ExtensionMethods 6 | { 7 | public static class TypeExtensions 8 | { 9 | private const BindingFlags Flags = 10 | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy; 11 | 12 | public static T Create(this Type type, params object[] parameters) 13 | { 14 | var constructor = type.GetConstructor(Flags, null, 15 | parameters != null 16 | ? parameters.Select(o => o.GetType()).ToArray() 17 | : Type.EmptyTypes, 18 | null); 19 | 20 | return constructor.Invoke(parameters).CastTo(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LinqToLdap.Tests/TestSupport/MockQueryCommandFactory.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Mapping; 2 | using LinqToLdap.QueryCommands; 3 | using LinqToLdap.QueryCommands.Options; 4 | 5 | namespace LinqToLdap.Tests.TestSupport 6 | { 7 | public class MockQueryCommandFactory : IQueryCommandFactory 8 | { 9 | public QueryCommandType Type { get; set; } 10 | public IQueryCommandOptions Options { get; set; } 11 | public IObjectMapping Mapping { get; set; } 12 | 13 | public IQueryCommand QueryCommandToReturn { get; set; } 14 | 15 | public IQueryCommand GetCommand(QueryCommandType type, IQueryCommandOptions options, IObjectMapping mapping) 16 | { 17 | Type = type; 18 | Options = options; 19 | Mapping = mapping; 20 | 21 | return QueryCommandToReturn; 22 | } 23 | 24 | public string Filter 25 | { 26 | get 27 | { 28 | return Options != null ? Options.Filter : null; 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LinqToLdap/Collections/ILdapPage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace LinqToLdap.Collections 4 | { 5 | /// 6 | /// Represents a page of items 7 | /// 8 | /// The type of items 9 | public interface ILdapPage : IEnumerable 10 | { 11 | /// 12 | /// The actual number of items in the page. 13 | /// 14 | int Count { get; } 15 | 16 | /// 17 | /// The size of the page. 18 | /// 19 | int PageSize { get; } 20 | 21 | /// 22 | /// Indicates if there is another page. 23 | /// 24 | bool HasNextPage { get; } 25 | 26 | /// 27 | /// The cookie for the next page. 28 | /// 29 | byte[] NextPage { get; } 30 | 31 | /// 32 | /// The LDAP Filter used to produce this page. 33 | /// 34 | string Filter { get; } 35 | } 36 | } -------------------------------------------------------------------------------- /LinqToLdap/Logging/ILinqToLdapLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.DirectoryServices.Protocols; 3 | 4 | namespace LinqToLdap.Logging 5 | { 6 | /// 7 | /// Interface for logging and information. 8 | /// 9 | public interface ILinqToLdapLogger 10 | { 11 | /// 12 | /// Allows changing and checking if trace logging is enabled. 13 | /// 14 | bool TraceEnabled { get; set; } 15 | 16 | /// 17 | /// Logs query information if is true. 18 | /// 19 | /// 20 | void Trace(string message); 21 | 22 | /// 23 | /// Writes the optional message and exception to the log. 24 | /// 25 | /// The message. 26 | /// The exception. 27 | void Error(Exception ex, string message = null); 28 | } 29 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/Options/SortingOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | 5 | namespace LinqToLdap.QueryCommands.Options 6 | { 7 | internal class SortingOptions : ISortingOptions 8 | { 9 | private readonly List _sortKeys; 10 | private SortKey _sortKey; 11 | 12 | public SortingOptions() 13 | { 14 | _sortKeys = new List(); 15 | } 16 | 17 | public void AddSort(string attributeName, bool descending) 18 | { 19 | if (attributeName.IsNullOrEmpty()) throw new ArgumentNullException(nameof(attributeName)); 20 | 21 | _sortKey = new SortKey { AttributeName = attributeName, ReverseOrder = descending }; 22 | _sortKeys.Add(_sortKey); 23 | } 24 | 25 | public void SetMatchingRule(string matchingRule) 26 | { 27 | _sortKey.MatchingRule = matchingRule; 28 | } 29 | 30 | public SortKey[] Keys { get { return _sortKeys.ToArray(); } } 31 | } 32 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/Resources/cert.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICvDCCAiUCEEoZ0jiMglkcpV1zXxVd3KMwDQYJKoZIhvcNAQEEBQAwgZ4xHzAd 3 | BgNVBAoTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxFzAVBgNVBAsTDlZlcmlTaWdu 4 | LCBJbmMuMSwwKgYDVQQLEyNWZXJpU2lnbiBUaW1lIFN0YW1waW5nIFNlcnZpY2Ug 5 | Um9vdDE0MDIGA1UECxMrTk8gTElBQklMSVRZIEFDQ0VQVEVELCAoYyk5NyBWZXJp 6 | U2lnbiwgSW5jLjAeFw05NzA1MTIwMDAwMDBaFw0wNDAxMDcyMzU5NTlaMIGeMR8w 7 | HQYDVQQKExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMRcwFQYDVQQLEw5WZXJpU2ln 8 | biwgSW5jLjEsMCoGA1UECxMjVmVyaVNpZ24gVGltZSBTdGFtcGluZyBTZXJ2aWNl 9 | IFJvb3QxNDAyBgNVBAsTK05PIExJQUJJTElUWSBBQ0NFUFRFRCwgKGMpOTcgVmVy 10 | aVNpZ24sIEluYy4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANMuIPBofCwt 11 | LoEcsQaypwu3EQ1X2lPYdePJMyqy1PYJWzTz6ZD+CQzQ2xtauc3n9oixncCHJet9 12 | WBBzanjLcRX9xlj2KatYXpYE/S1iEViBHMpxlNUiWC/VzBQFhDa6lKq0TUrp7jsi 13 | rVaZfiGcbIbASkeXarSmNtX8CS3TtDmbAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEA 14 | YVUOPnvHkhJ+ERCOIszUsxMrW+hE5At4nqR+86cHch7iWe/MhOOJlEzbTmHvs6T7 15 | Rj1QNAufcFb2jip/F87lY795aQdzLrCVKIr17aqp0l3NCsoQCY/Os68olsR5KYSS 16 | 3P+6Z0JIppAQ5L9h+JxT5ZPRcz/4/Z1PhKxV0f0RY2M= 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /LinqToLdap.NET35.Tests/Resources/cert.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICvDCCAiUCEEoZ0jiMglkcpV1zXxVd3KMwDQYJKoZIhvcNAQEEBQAwgZ4xHzAd 3 | BgNVBAoTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxFzAVBgNVBAsTDlZlcmlTaWdu 4 | LCBJbmMuMSwwKgYDVQQLEyNWZXJpU2lnbiBUaW1lIFN0YW1waW5nIFNlcnZpY2Ug 5 | Um9vdDE0MDIGA1UECxMrTk8gTElBQklMSVRZIEFDQ0VQVEVELCAoYyk5NyBWZXJp 6 | U2lnbiwgSW5jLjAeFw05NzA1MTIwMDAwMDBaFw0wNDAxMDcyMzU5NTlaMIGeMR8w 7 | HQYDVQQKExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMRcwFQYDVQQLEw5WZXJpU2ln 8 | biwgSW5jLjEsMCoGA1UECxMjVmVyaVNpZ24gVGltZSBTdGFtcGluZyBTZXJ2aWNl 9 | IFJvb3QxNDAyBgNVBAsTK05PIExJQUJJTElUWSBBQ0NFUFRFRCwgKGMpOTcgVmVy 10 | aVNpZ24sIEluYy4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANMuIPBofCwt 11 | LoEcsQaypwu3EQ1X2lPYdePJMyqy1PYJWzTz6ZD+CQzQ2xtauc3n9oixncCHJet9 12 | WBBzanjLcRX9xlj2KatYXpYE/S1iEViBHMpxlNUiWC/VzBQFhDa6lKq0TUrp7jsi 13 | rVaZfiGcbIbASkeXarSmNtX8CS3TtDmbAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEA 14 | YVUOPnvHkhJ+ERCOIszUsxMrW+hE5At4nqR+86cHch7iWe/MhOOJlEzbTmHvs6T7 15 | Rj1QNAufcFb2jip/F87lY795aQdzLrCVKIr17aqp0l3NCsoQCY/Os68olsR5KYSS 16 | 3P+6Z0JIppAQ5L9h+JxT5ZPRcz/4/Z1PhKxV0f0RY2M= 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /LinqToLdap.NET40.Tests/Resources/cert.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICvDCCAiUCEEoZ0jiMglkcpV1zXxVd3KMwDQYJKoZIhvcNAQEEBQAwgZ4xHzAd 3 | BgNVBAoTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxFzAVBgNVBAsTDlZlcmlTaWdu 4 | LCBJbmMuMSwwKgYDVQQLEyNWZXJpU2lnbiBUaW1lIFN0YW1waW5nIFNlcnZpY2Ug 5 | Um9vdDE0MDIGA1UECxMrTk8gTElBQklMSVRZIEFDQ0VQVEVELCAoYyk5NyBWZXJp 6 | U2lnbiwgSW5jLjAeFw05NzA1MTIwMDAwMDBaFw0wNDAxMDcyMzU5NTlaMIGeMR8w 7 | HQYDVQQKExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMRcwFQYDVQQLEw5WZXJpU2ln 8 | biwgSW5jLjEsMCoGA1UECxMjVmVyaVNpZ24gVGltZSBTdGFtcGluZyBTZXJ2aWNl 9 | IFJvb3QxNDAyBgNVBAsTK05PIExJQUJJTElUWSBBQ0NFUFRFRCwgKGMpOTcgVmVy 10 | aVNpZ24sIEluYy4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANMuIPBofCwt 11 | LoEcsQaypwu3EQ1X2lPYdePJMyqy1PYJWzTz6ZD+CQzQ2xtauc3n9oixncCHJet9 12 | WBBzanjLcRX9xlj2KatYXpYE/S1iEViBHMpxlNUiWC/VzBQFhDa6lKq0TUrp7jsi 13 | rVaZfiGcbIbASkeXarSmNtX8CS3TtDmbAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEA 14 | YVUOPnvHkhJ+ERCOIszUsxMrW+hE5At4nqR+86cHch7iWe/MhOOJlEzbTmHvs6T7 15 | Rj1QNAufcFb2jip/F87lY795aQdzLrCVKIr17aqp0l3NCsoQCY/Os68olsR5KYSS 16 | 3P+6Z0JIppAQ5L9h+JxT5ZPRcz/4/Z1PhKxV0f0RY2M= 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /LinqToLdap.NET45.Tests/Resources/cert.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICvDCCAiUCEEoZ0jiMglkcpV1zXxVd3KMwDQYJKoZIhvcNAQEEBQAwgZ4xHzAd 3 | BgNVBAoTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxFzAVBgNVBAsTDlZlcmlTaWdu 4 | LCBJbmMuMSwwKgYDVQQLEyNWZXJpU2lnbiBUaW1lIFN0YW1waW5nIFNlcnZpY2Ug 5 | Um9vdDE0MDIGA1UECxMrTk8gTElBQklMSVRZIEFDQ0VQVEVELCAoYyk5NyBWZXJp 6 | U2lnbiwgSW5jLjAeFw05NzA1MTIwMDAwMDBaFw0wNDAxMDcyMzU5NTlaMIGeMR8w 7 | HQYDVQQKExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMRcwFQYDVQQLEw5WZXJpU2ln 8 | biwgSW5jLjEsMCoGA1UECxMjVmVyaVNpZ24gVGltZSBTdGFtcGluZyBTZXJ2aWNl 9 | IFJvb3QxNDAyBgNVBAsTK05PIExJQUJJTElUWSBBQ0NFUFRFRCwgKGMpOTcgVmVy 10 | aVNpZ24sIEluYy4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANMuIPBofCwt 11 | LoEcsQaypwu3EQ1X2lPYdePJMyqy1PYJWzTz6ZD+CQzQ2xtauc3n9oixncCHJet9 12 | WBBzanjLcRX9xlj2KatYXpYE/S1iEViBHMpxlNUiWC/VzBQFhDa6lKq0TUrp7jsi 13 | rVaZfiGcbIbASkeXarSmNtX8CS3TtDmbAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEA 14 | YVUOPnvHkhJ+ERCOIszUsxMrW+hE5At4nqR+86cHch7iWe/MhOOJlEzbTmHvs6T7 15 | Rj1QNAufcFb2jip/F87lY795aQdzLrCVKIr17aqp0l3NCsoQCY/Os68olsR5KYSS 16 | 3P+6Z0JIppAQ5L9h+JxT5ZPRcz/4/Z1PhKxV0f0RY2M= 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /LinqToLdap/Transformers/DynamicResultTransformer.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Collections; 2 | using LinqToLdap.Helpers; 3 | using System; 4 | using System.DirectoryServices.Protocols; 5 | 6 | namespace LinqToLdap.Transformers 7 | { 8 | internal class DynamicResultTransformer : IResultTransformer 9 | { 10 | private readonly SelectProjection _projection; 11 | 12 | public DynamicResultTransformer(SelectProjection projection = null) 13 | { 14 | _projection = projection; 15 | } 16 | 17 | public object Transform(SearchResultEntry entry) 18 | { 19 | var attributes = new DirectoryAttributes(entry); 20 | 21 | return _projection == null ? attributes : _projection.Projection.DynamicInvoke(attributes); 22 | } 23 | 24 | public object Default() 25 | { 26 | if (_projection != null) 27 | { 28 | var type = _projection.ReturnType; 29 | return type.IsValueType ? Activator.CreateInstance(type) : null; 30 | } 31 | 32 | return null; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Alan Hatter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LinqToLdap.Tests/Extensions/TypeExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Mapping; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using SharpTestsEx; 4 | 5 | namespace LinqToLdap.Tests.Extensions 6 | { 7 | [TestClass] 8 | [DirectorySchema("name")] 9 | public class TypeExtensionsTest 10 | { 11 | [TestMethod] 12 | public void IsAnonymous_NonAnonymousClass_ReturnsFalse() 13 | { 14 | GetType().IsAnonymous().Should().Be.False(); 15 | } 16 | 17 | [TestMethod] 18 | public void IsAnonymous_AnonymousClass_ReturnsTrue() 19 | { 20 | var anon = new {Prop = ""}; 21 | 22 | anon.GetType().IsAnonymous().Should().Be.True(); 23 | } 24 | 25 | [TestMethod] 26 | public void HasDirectorySchema_ClassWithDirectorySchema_ReturnsTrue() 27 | { 28 | GetType().HasDirectorySchema().Should().Be.True(); 29 | } 30 | 31 | [TestMethod] 32 | public void HasDirectorySchema_ClassWithoutDirectorySchema_ReturnsFalse() 33 | { 34 | var anon = new { Prop = "" }; 35 | 36 | anon.GetType().HasDirectorySchema().Should().Be.False(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LinqToLdap/Collections/SerializableKeyValuePair.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace LinqToLdap.Collections 5 | { 6 | /// 7 | /// A serializable class implementation based on 8 | /// 9 | /// The type of the key 10 | /// The type of the value 11 | [Serializable] 12 | public class SerializableKeyValuePair 13 | { 14 | /// 15 | /// Default parameterless constructor. 16 | /// 17 | public SerializableKeyValuePair() { } 18 | 19 | /// 20 | /// Default constructor with initial values. 21 | /// 22 | public SerializableKeyValuePair(TKey key, TValue value) 23 | { 24 | Key = key; 25 | Value = value; 26 | } 27 | 28 | /// 29 | /// Key property. 30 | /// 31 | public TKey Key { get; set; } 32 | 33 | /// 34 | /// Value property. 35 | /// 36 | public TValue Value { get; set; } 37 | } 38 | } -------------------------------------------------------------------------------- /LinqToLdap/Transformers/ProjectionResultTransformer.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Helpers; 2 | using LinqToLdap.Mapping; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace LinqToLdap.Transformers 7 | { 8 | internal class ProjectionResultTransformer : ResultTransformer 9 | { 10 | private readonly SelectProjection _selectProjection; 11 | 12 | public ProjectionResultTransformer(SelectProjection selectProjection, IDictionary queriedProperties, IObjectMapping mapping) 13 | : base(queriedProperties, mapping, queriedProperties.Count == mapping.Properties.Count && selectProjection.ReturnType == mapping.Type) 14 | { 15 | _selectProjection = selectProjection; 16 | } 17 | 18 | public override object Transform(System.DirectoryServices.Protocols.SearchResultEntry entry) 19 | { 20 | var transformed = _selectProjection.Projection.DynamicInvoke(base.Transform(entry)); 21 | 22 | return transformed; 23 | } 24 | 25 | public override object Default() 26 | { 27 | var type = _selectProjection.ReturnType; 28 | return type.IsValueType ? Activator.CreateInstance(type) : null; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/QueryCommandType.cs: -------------------------------------------------------------------------------- 1 | namespace LinqToLdap.QueryCommands 2 | { 3 | /// 4 | /// Indicates the type of command to create. 5 | /// 6 | public enum QueryCommandType 7 | { 8 | /// 9 | /// Represents a standard command 10 | /// 11 | StandardCommand = 0, 12 | 13 | /// 14 | /// Any command 15 | /// 16 | AnyCommand = 1, 17 | 18 | /// 19 | /// First / FirstOrDefault command 20 | /// 21 | FirstOrDefaultCommand = 2, 22 | 23 | /// 24 | /// Single command 25 | /// 26 | SingleCommand = 3, 27 | 28 | /// 29 | /// SingleOrDefault command 30 | /// 31 | SingleOrDefaultCommand = 4, 32 | 33 | /// 34 | /// Count command 35 | /// 36 | CountCommand = 5, 37 | 38 | /// 39 | /// GetRequest command 40 | /// 41 | GetRequestCommand = 6, 42 | 43 | /// 44 | /// FirstCommand command 45 | /// 46 | FirstCommand = 7 47 | } 48 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/IPropertyMappingBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace LinqToLdap.Mapping 4 | { 5 | /// 6 | /// Interface for class that can build 7 | /// 8 | public interface IPropertyMappingBuilder 9 | { 10 | /// 11 | /// Name of the property 12 | /// 13 | string PropertyName { get; } 14 | 15 | /// 16 | /// Indicates if the builder is for a distinguished name property. 17 | /// 18 | bool IsDistinguishedName { get; } 19 | 20 | /// 21 | /// Indicates if the property is read only 22 | /// 23 | ReadOnly? ReadOnlyConfiguration { get; } 24 | 25 | /// 26 | /// The name of the attribute in the directory. 27 | /// 28 | string AttributeName { get; } 29 | 30 | /// 31 | /// The . 32 | /// 33 | PropertyInfo PropertyInfo { get; } 34 | 35 | /// 36 | /// Create the property mapping 37 | /// 38 | /// 39 | IPropertyMapping ToPropertyMapping(); 40 | } 41 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/TestSupport/MockLdapConnection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | 5 | namespace LinqToLdap.Tests.TestSupport 6 | { 7 | public class MockLdapConnection : LdapConnection 8 | { 9 | public MockLdapConnection(Dictionary requestResposnes = null) : base("localhost") 10 | { 11 | RequestResponses = requestResposnes ?? new Dictionary(); 12 | SentRequests = new List(); 13 | } 14 | 15 | public Exception ExceptionToThrow { get; set; } 16 | public Dictionary RequestResponses { get; private set; } 17 | public List SentRequests { get; private set; } 18 | 19 | public override DirectoryResponse SendRequest(DirectoryRequest request) 20 | { 21 | if (ExceptionToThrow != null) throw ExceptionToThrow; 22 | SentRequests.Add(request); 23 | return RequestResponses[request.GetType()]; 24 | } 25 | 26 | protected override void Dispose(bool disposing) 27 | { 28 | base.Dispose(disposing); 29 | TimesDisposed += 1; 30 | } 31 | 32 | public int TimesDisposed { get; private set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/Options/DynamicQueryCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Collections; 2 | using LinqToLdap.Transformers; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace LinqToLdap.QueryCommands.Options 8 | { 9 | internal class DynamicQueryCommandOptions : QueryCommandOptions 10 | { 11 | private readonly SelectProjection _projection; 12 | 13 | public DynamicQueryCommandOptions(IEnumerable attrubutes) 14 | : base(attrubutes == null ? new Dictionary() : attrubutes.ToDictionary(a => a)) 15 | { 16 | } 17 | 18 | public DynamicQueryCommandOptions(SelectProjection projection) 19 | : base(projection == null ? new Dictionary() : projection.SelectedProperties) 20 | { 21 | _projection = projection; 22 | } 23 | 24 | public DynamicQueryCommandOptions() 25 | : base(new Dictionary()) 26 | { 27 | } 28 | 29 | public override Type GetEnumeratorReturnType() 30 | { 31 | return _projection == null ? typeof(IDirectoryAttributes) : _projection.ReturnType; 32 | } 33 | 34 | public override IResultTransformer GetTransformer() 35 | { 36 | return new DynamicResultTransformer(_projection); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/Resources/cert2.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc 3 | MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP 4 | bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2 5 | MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft 6 | ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg 7 | Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP 8 | ADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lk 9 | hsmj76CGv2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym 10 | 1BW32J/X3HGrfpq/m44zDyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsW 11 | OqMFf6Dch9Wc/HKpoH145LcxVR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb 12 | 2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQko 13 | O3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUw 14 | AwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAU 15 | AK3Zo/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB 16 | BQUAA4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF 17 | Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAb 18 | LjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTIdGcL+oir 19 | oQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43gKd8hdIaC2y+C 20 | MMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds 21 | sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /LinqToLdap.NET35.Tests/Resources/cert2.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc 3 | MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP 4 | bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2 5 | MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft 6 | ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg 7 | Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP 8 | ADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lk 9 | hsmj76CGv2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym 10 | 1BW32J/X3HGrfpq/m44zDyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsW 11 | OqMFf6Dch9Wc/HKpoH145LcxVR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb 12 | 2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQko 13 | O3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUw 14 | AwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAU 15 | AK3Zo/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB 16 | BQUAA4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF 17 | Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAb 18 | LjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTIdGcL+oir 19 | oQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43gKd8hdIaC2y+C 20 | MMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds 21 | sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /LinqToLdap.NET40.Tests/Resources/cert2.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc 3 | MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP 4 | bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2 5 | MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft 6 | ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg 7 | Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP 8 | ADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lk 9 | hsmj76CGv2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym 10 | 1BW32J/X3HGrfpq/m44zDyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsW 11 | OqMFf6Dch9Wc/HKpoH145LcxVR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb 12 | 2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQko 13 | O3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUw 14 | AwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAU 15 | AK3Zo/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB 16 | BQUAA4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF 17 | Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAb 18 | LjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTIdGcL+oir 19 | oQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43gKd8hdIaC2y+C 20 | MMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds 21 | sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /LinqToLdap.NET45.Tests/Resources/cert2.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc 3 | MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP 4 | bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2 5 | MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft 6 | ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg 7 | Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP 8 | ADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lk 9 | hsmj76CGv2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym 10 | 1BW32J/X3HGrfpq/m44zDyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsW 11 | OqMFf6Dch9Wc/HKpoH145LcxVR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb 12 | 2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQko 13 | O3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUw 14 | AwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAU 15 | AK3Zo/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB 16 | BQUAA4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF 17 | Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAb 18 | LjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTIdGcL+oir 19 | oQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43gKd8hdIaC2y+C 20 | MMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds 21 | sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /LinqToLdap/Collections/OriginalValuesCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | 6 | namespace LinqToLdap.Collections 7 | { 8 | /// 9 | /// Represents a collection 10 | /// 11 | [Serializable] 12 | public class OriginalValuesCollection : Collection> 13 | { 14 | /// 15 | /// Default constructor 16 | /// 17 | public OriginalValuesCollection() : base(new List>()) { } 18 | 19 | /// 20 | /// Constructor that takes list of values. 21 | /// 22 | /// 23 | public OriginalValuesCollection(IList> values) : base(values) { } 24 | 25 | /// 26 | /// Case insensitive key accessor for the value. 27 | /// 28 | /// The property name 29 | /// 30 | public object this[String key] 31 | { 32 | get 33 | { 34 | return this.Where(kvp => kvp.Key.Equals(key, StringComparison.OrdinalIgnoreCase)) 35 | .Select(kvp => kvp.Value) 36 | .FirstOrDefault(); 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/StringPropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace LinqToLdap.Mapping.PropertyMappings 4 | { 5 | internal class StringPropertyMapping : PropertyMappingGeneric where T : class 6 | { 7 | public StringPropertyMapping(PropertyMappingArguments arguments) 8 | : base(arguments) 9 | { 10 | } 11 | 12 | public override string FormatValueToFilter(object value) 13 | { 14 | return value.ToString().CleanFilterValue(); 15 | } 16 | 17 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 18 | { 19 | var modification = new DirectoryAttributeModification 20 | { 21 | Name = AttributeName, 22 | Operation = DirectoryAttributeOperation.Replace 23 | }; 24 | var value = (string)GetValueForDirectory(instance); 25 | 26 | if (value != null) 27 | { 28 | modification.Add(value); 29 | } 30 | 31 | return modification; 32 | } 33 | 34 | public override object GetValueForDirectory(object instance) 35 | { 36 | return GetValue(instance); 37 | } 38 | 39 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 40 | { 41 | return value != null ? value.Count == 0 ? string.Empty : value[0] : null; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/AnonymousObjectMapping.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Helpers; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace LinqToLdap.Mapping 7 | { 8 | internal class AnonymousObjectMapping : ObjectMapping where T : class 9 | { 10 | private readonly CtorWithParams _constructor; 11 | 12 | public AnonymousObjectMapping(string namingContext, 13 | IEnumerable propertyMappings, string objectCategory, bool includeObjectCategory, IEnumerable objectClass, bool includeObjectClasses) 14 | : base(namingContext, propertyMappings, objectCategory, includeObjectCategory, objectClass, includeObjectClasses) 15 | { 16 | _constructor = DelegateBuilder.BuildCtorWithParams(typeof(T).GetConstructors().First()); 17 | } 18 | 19 | public override bool IsForAnonymousType { get { return true; } } 20 | 21 | public override Type Type => typeof(T); 22 | 23 | public override object Create(object[] parameters = null, object[] objectClasses = null) 24 | { 25 | if (objectClasses != null) throw new NotSupportedException("Anonymous objects can't support sub types"); 26 | 27 | T instance = _constructor(parameters); 28 | return instance; 29 | } 30 | 31 | public override void AddSubTypeMapping(IObjectMapping mapping) 32 | { 33 | throw new NotSupportedException("Anonymous objects can't support sub types"); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /LinqToLdap.NET35.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("LinqToLdap.NET35.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("LinqToLdap.NET35.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("39224a0d-3eac-4de4-a598-7a0173648683")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /LinqToLdap.NET40.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("LinqToLdap.NET40.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("LinqToLdap.NET40.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("cf5d7ebe-db3b-4c67-b302-d656dfe32ba2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/CatchAllPropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.DirectoryServices.Protocols; 3 | 4 | namespace LinqToLdap.Mapping.PropertyMappings 5 | { 6 | internal class CatchAllPropertyMapping : PropertyMappingGeneric where T : class 7 | { 8 | public CatchAllPropertyMapping(PropertyMappingArguments arguments) 9 | : base(arguments) 10 | { 11 | } 12 | 13 | public override string FormatValueToFilter(object value) 14 | { 15 | throw new NotSupportedException("Catch All property does not support this operation."); 16 | } 17 | 18 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 19 | { 20 | throw new NotSupportedException("Catch All property does not support this operation."); 21 | } 22 | 23 | public override object GetValueForDirectory(object instance) 24 | { 25 | throw new NotSupportedException("Catch All property does not support this operation."); 26 | } 27 | 28 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 29 | { 30 | throw new NotSupportedException("Catch All property does not support this operation."); 31 | } 32 | 33 | public override bool IsEqual(object instance, object value, out DirectoryAttributeModification modification) 34 | { 35 | throw new NotSupportedException("Catch All property does not support this operation."); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /LinqToLdap/Visitors/DynamicSelectProjector.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | 5 | namespace LinqToLdap.Visitors 6 | { 7 | internal class DynamicSelectProjector : SelectProjector 8 | { 9 | public DynamicSelectProjector(IDictionary mappedProperties) : base(mappedProperties) 10 | { 11 | } 12 | 13 | protected override Expression VisitMemberAccess(MemberExpression m) 14 | { 15 | var methodCall = m.Expression as MethodCallExpression; 16 | if (methodCall != null) 17 | { 18 | if (methodCall.Method.DeclaringType == typeof(IDirectoryAttributes)) 19 | { 20 | VisitMethodCall(methodCall); 21 | } 22 | } 23 | else if (m.Member.Name == "DistinguishedName") 24 | { 25 | Properties["DistinguishedName"] = "DistinguishedName"; 26 | } 27 | return m; 28 | } 29 | 30 | protected override Expression VisitMethodCall(MethodCallExpression m) 31 | { 32 | if (m.Method.DeclaringType == typeof(IDirectoryAttributes) && m.Method.Name.StartsWith("Get") && 33 | m.Arguments.Count > 0 && m.Arguments[0] is ConstantExpression) 34 | { 35 | var value = ((ConstantExpression)m.Arguments[0]).Value.ToString(); 36 | Properties[value] = value; 37 | return m; 38 | } 39 | 40 | return base.VisitMethodCall(m); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/LinqToLdap.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | true 9 | 10 | linqtoldap_tests.snk 11 | 12 | false 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | True 32 | True 33 | Resources.resx 34 | 35 | 36 | 37 | 38 | 39 | ResXFileCodeGenerator 40 | Resources.Designer.cs 41 | 42 | 43 | -------------------------------------------------------------------------------- /LinqToLdap/ExposeInternals.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("LinqToLdap.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001007f6ffc5b29dd6fba774e22784dd8abc5d72c40f4c43f550d6dc92383cd21fd05724699429e9bffb5d38a471f27236c0f363731823559c9a75a9be93a0209783b18e19b3261305e2a19b04f7f3c71e2f4339865991732bf98c85e1798ac956853b73e271520be894d42a1e7386379a22c996e5cfd2bfe622e3aa3cc4ef964a5c8", AllInternalsVisible = true)] 4 | [assembly: InternalsVisibleTo("LinqToLdap.NET35.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001007f6ffc5b29dd6fba774e22784dd8abc5d72c40f4c43f550d6dc92383cd21fd05724699429e9bffb5d38a471f27236c0f363731823559c9a75a9be93a0209783b18e19b3261305e2a19b04f7f3c71e2f4339865991732bf98c85e1798ac956853b73e271520be894d42a1e7386379a22c996e5cfd2bfe622e3aa3cc4ef964a5c8", AllInternalsVisible = true)] 5 | [assembly: InternalsVisibleTo("LinqToLdap.NET40.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001007f6ffc5b29dd6fba774e22784dd8abc5d72c40f4c43f550d6dc92383cd21fd05724699429e9bffb5d38a471f27236c0f363731823559c9a75a9be93a0209783b18e19b3261305e2a19b04f7f3c71e2f4339865991732bf98c85e1798ac956853b73e271520be894d42a1e7386379a22c996e5cfd2bfe622e3aa3cc4ef964a5c8", AllInternalsVisible = true)] 6 | [assembly: InternalsVisibleTo("LinqToLdap.NET45.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001007f6ffc5b29dd6fba774e22784dd8abc5d72c40f4c43f550d6dc92383cd21fd05724699429e9bffb5d38a471f27236c0f363731823559c9a75a9be93a0209783b18e19b3261305e2a19b04f7f3c71e2f4339865991732bf98c85e1798ac956853b73e271520be894d42a1e7386379a22c996e5cfd2bfe622e3aa3cc4ef964a5c8", AllInternalsVisible = true)] -------------------------------------------------------------------------------- /LinqToLdap/Visitors/SelectProjector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | 5 | namespace LinqToLdap.Visitors 6 | { 7 | internal class SelectProjector : ExpressionVisitor 8 | { 9 | protected readonly IDictionary MappedProperties; 10 | protected readonly IDictionary Properties; 11 | protected SelectProjection Projection; 12 | 13 | public SelectProjector(IDictionary mappedProperties) 14 | { 15 | Properties = new Dictionary(); 16 | MappedProperties = mappedProperties; 17 | } 18 | 19 | public virtual SelectProjection ProjectProperties(LambdaExpression p) 20 | { 21 | Visit(p); 22 | Projection = Properties.Count == 0 23 | ? new SelectProjection(MappedProperties, p) 24 | : new SelectProjection(Properties, p); 25 | return Projection; 26 | } 27 | 28 | protected override Expression VisitMemberAccess(MemberExpression m) 29 | { 30 | if (m.Expression != null && (m.Expression.NodeType == ExpressionType.Parameter || m.Expression.NodeType == ExpressionType.TypeAs || m.Expression.NodeType == ExpressionType.Convert)) 31 | { 32 | string name; 33 | Properties[m.Member.Name] = MappedProperties.TryGetValue(m.Member.Name, out name) ? name : m.Member.Name; 34 | 35 | return m; 36 | } 37 | throw new NotSupportedException(string.Format("The member '{0}' is not supported", m.Member.Name)); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/QueryCommandFactory.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Mapping; 2 | using LinqToLdap.QueryCommands.Options; 3 | using System; 4 | 5 | namespace LinqToLdap.QueryCommands 6 | { 7 | internal class QueryCommandFactory : IQueryCommandFactory 8 | { 9 | public IQueryCommand GetCommand(QueryCommandType type, IQueryCommandOptions options, IObjectMapping mapping) 10 | { 11 | switch (type) 12 | { 13 | case QueryCommandType.StandardCommand: 14 | return new StandardQueryCommand(options, mapping); 15 | 16 | case QueryCommandType.AnyCommand: 17 | return new AnyQueryCommand(options, mapping); 18 | 19 | case QueryCommandType.CountCommand: 20 | return new CountQueryCommand(options, mapping); 21 | 22 | case QueryCommandType.FirstOrDefaultCommand: 23 | return new FirstOrDefaultQueryCommand(options, mapping); 24 | 25 | case QueryCommandType.FirstCommand: 26 | return new FirstQueryCommand(options, mapping); 27 | 28 | case QueryCommandType.SingleCommand: 29 | return new SingleQueryCommand(options, mapping); 30 | 31 | case QueryCommandType.SingleOrDefaultCommand: 32 | return new SingleOrDefaultQueryCommand(options, mapping); 33 | 34 | case QueryCommandType.GetRequestCommand: 35 | return new GetRequestCommand(options, mapping); 36 | 37 | default: 38 | throw new NotSupportedException(string.Format("QueryCommandType '{0}' is not supported.", type)); 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryProvider.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Helpers; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | 5 | namespace LinqToLdap 6 | { 7 | internal abstract class QueryProvider : IAsyncQueryProvider 8 | { 9 | public IQueryable CreateQuery(Expression expression) 10 | { 11 | var elementType = TypeSystem.GetElementType(expression.Type); 12 | 13 | return (IQueryable)ObjectActivator.CreateGenericInstance(typeof(DirectoryQuery<>), elementType, new object[] { this, expression }, null); 14 | } 15 | 16 | public IQueryable CreateQuery(Expression expression) 17 | { 18 | return new DirectoryQuery(this, expression); 19 | } 20 | 21 | object IQueryProvider.Execute(Expression expression) 22 | { 23 | return Execute(expression); 24 | } 25 | 26 | public TResult Execute(Expression expression) 27 | { 28 | return (TResult)Execute(expression); 29 | } 30 | 31 | #if (!NET35 && !NET40) 32 | 33 | async System.Threading.Tasks.Task IAsyncQueryProvider.ExecuteAsync(Expression expression) 34 | { 35 | return await ExecuteAsync(expression).ConfigureAwait(false); 36 | } 37 | 38 | public async System.Threading.Tasks.Task ExecuteAsync(Expression expression) 39 | { 40 | return (TResult)await ExecuteAsync(expression).ConfigureAwait(false); 41 | } 42 | 43 | public abstract System.Threading.Tasks.Task ExecuteAsync(Expression expression); 44 | 45 | #endif 46 | 47 | public abstract string GetQueryText(Expression expression); 48 | 49 | public abstract object Execute(Expression expression); 50 | } 51 | } -------------------------------------------------------------------------------- /LinqToLdap/Helpers/TypeSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace LinqToLdap.Helpers 6 | { 7 | /// 8 | /// Pulled from http://msdn.microsoft.com/en-us/library/bb546158.aspx 9 | /// 10 | internal static class TypeSystem 11 | { 12 | internal static Type GetElementType(Type seqType) 13 | { 14 | Type ienum = FindIEnumerable(seqType); 15 | return ienum == null ? seqType : ienum.GetGenericArguments()[0]; 16 | } 17 | 18 | private static Type FindIEnumerable(Type seqType) 19 | { 20 | if (seqType == null || seqType == typeof(string)) 21 | return null; 22 | 23 | if (seqType.IsArray) 24 | return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType()); 25 | 26 | if (seqType.IsGenericType) 27 | { 28 | foreach (Type ienum in 29 | seqType.GetGenericArguments().Select(arg => typeof(IEnumerable<>).MakeGenericType(arg)).Where(ienum => ienum.IsAssignableFrom(seqType))) 30 | { 31 | return ienum; 32 | } 33 | } 34 | 35 | Type[] ifaces = seqType.GetInterfaces(); 36 | if (ifaces != null && ifaces.Length > 0) 37 | { 38 | foreach (Type ienum in ifaces.Select(FindIEnumerable).Where(ienum => ienum != null)) 39 | { 40 | return ienum; 41 | } 42 | } 43 | 44 | if (seqType.BaseType != null && seqType.BaseType != typeof(object)) 45 | { 46 | return FindIEnumerable(seqType.BaseType); 47 | } 48 | 49 | return null; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/GetRequestCommand.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Logging; 2 | using LinqToLdap.Mapping; 3 | using LinqToLdap.QueryCommands.Options; 4 | using System; 5 | using System.DirectoryServices.Protocols; 6 | 7 | namespace LinqToLdap.QueryCommands 8 | { 9 | internal class GetRequestCommand : QueryCommand 10 | { 11 | public GetRequestCommand(IQueryCommandOptions options, IObjectMapping mapping) : base(options, mapping, true) 12 | { 13 | } 14 | 15 | public override object Execute(DirectoryConnection connection, SearchScope scope, int maxPageSize, bool pagingEnabled, ILinqToLdapLogger log = null, string namingContext = null) 16 | { 17 | SetDistinguishedName(namingContext); 18 | SearchRequest.Scope = scope; 19 | 20 | if (Options.SortingOptions != null) 21 | { 22 | if (GetControl(SearchRequest.Controls) != null) 23 | throw new InvalidOperationException("Only one sort request control can be sent to the server"); 24 | 25 | SearchRequest.Controls.Add(new SortRequestControl(Options.SortingOptions.Keys) { IsCritical = false }); 26 | } 27 | if (Options.TakeSize.HasValue && !Options.WithoutPaging) 28 | { 29 | SearchRequest.Controls.Add(new PageResultRequestControl(Options.TakeSize.Value)); 30 | } 31 | 32 | return SearchRequest; 33 | } 34 | 35 | #if !NET35 && !NET40 36 | 37 | public override System.Threading.Tasks.Task ExecuteAsync(LdapConnection connection, SearchScope scope, int maxPageSize, bool pagingEnabled, ILinqToLdapLogger log = null, string namingContext = null) 38 | { 39 | return System.Threading.Tasks.Task.FromResult(Execute(connection, scope, maxPageSize, pagingEnabled, log, namingContext)); 40 | } 41 | 42 | #endif 43 | } 44 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/Helpers/ObjectActivatorTests.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Collections; 2 | using LinqToLdap.Helpers; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using System; 5 | using System.Diagnostics; 6 | 7 | namespace LinqToLdap.Tests.Helpers 8 | { 9 | [TestClass] 10 | public class ObjectActivatorTests 11 | { 12 | private const int Iterations = 5000 * 100; 13 | 14 | [TestMethod] 15 | public void CreateGenericInstance_performance() 16 | { 17 | var watch = Stopwatch.StartNew(); 18 | for (int i = 0; i < Iterations; i++) 19 | { 20 | var parameters = new object[] 21 | { 22 | 100, 23 | new byte[0], 24 | new string[0], 25 | "" 26 | }; 27 | var inst = ObjectActivator.CreateGenericInstance(typeof(LdapPage<>), typeof(string), parameters, null); 28 | } 29 | watch.Stop(); 30 | Trace.WriteLine(watch.ElapsedMilliseconds); 31 | } 32 | 33 | [TestMethod] 34 | public void Activate_performance() 35 | { 36 | var watch = Stopwatch.StartNew(); 37 | for (int i = 0; i < Iterations; i++) 38 | { 39 | var parameters = new object[] 40 | { 41 | 100, 42 | new byte[0], 43 | new string[0], 44 | "" 45 | }; 46 | Activator.CreateInstance(typeof(LdapPage), parameters); 47 | } 48 | watch.Stop(); 49 | Trace.WriteLine(watch.ElapsedMilliseconds); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/NumericPropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.DirectoryServices.Protocols; 3 | 4 | namespace LinqToLdap.Mapping.PropertyMappings 5 | { 6 | internal class NumericPropertyMapping : PropertyMappingGeneric where T : class 7 | { 8 | public NumericPropertyMapping(PropertyMappingArguments arguments) : base(arguments) 9 | { 10 | } 11 | 12 | public override string FormatValueToFilter(object value) 13 | { 14 | return value.ToString(); 15 | } 16 | 17 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 18 | { 19 | var modification = new DirectoryAttributeModification 20 | { 21 | Name = AttributeName, 22 | Operation = DirectoryAttributeOperation.Replace 23 | }; 24 | var value = (string)GetValueForDirectory(instance); 25 | 26 | if (value != null) 27 | { 28 | modification.Add(value); 29 | } 30 | 31 | return modification; 32 | } 33 | 34 | public override object GetValueForDirectory(object instance) 35 | { 36 | var value = GetValue(instance); 37 | 38 | return value == null ? value : value.ToString(); 39 | } 40 | 41 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 42 | { 43 | if (value != null && value.Count > 0) 44 | { 45 | var num = value[0]; 46 | try 47 | { 48 | return Convert.ChangeType(num, UnderlyingType); 49 | } 50 | catch (Exception ex) 51 | { 52 | ThrowMappingException(num, dn, ex); 53 | } 54 | } 55 | 56 | AssertNullable(dn); 57 | 58 | return null; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/BooleanPropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace LinqToLdap.Mapping.PropertyMappings 4 | { 5 | internal class BooleanPropertyMapping : PropertyMappingGeneric where T : class 6 | { 7 | private const string True = "TRUE"; 8 | private const string False = "FALSE"; 9 | 10 | public BooleanPropertyMapping(PropertyMappingArguments arguments) : base(arguments) 11 | { 12 | } 13 | 14 | public override string FormatValueToFilter(object value) 15 | { 16 | return true.Equals(value) ? True : False; 17 | } 18 | 19 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 20 | { 21 | var modification = new DirectoryAttributeModification 22 | { 23 | Name = AttributeName, 24 | Operation = DirectoryAttributeOperation.Replace 25 | }; 26 | var value = (string)GetValueForDirectory(instance); 27 | 28 | if (value != null) 29 | { 30 | modification.Add(value); 31 | } 32 | 33 | return modification; 34 | } 35 | 36 | public override object GetValueForDirectory(object instance) 37 | { 38 | var value = GetValue(instance); 39 | 40 | if (value == null) return null; 41 | 42 | return true.Equals(value) ? True : False; 43 | } 44 | 45 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 46 | { 47 | if (value != null && value[0] is string str && !str.IsNullOrEmpty()) 48 | { 49 | if (True.Equals(str)) return true; 50 | if (False.Equals(str)) return false; 51 | 52 | ThrowMappingException(str, dn); 53 | } 54 | 55 | AssertNullable(dn); 56 | 57 | return null; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests.ClassMapAssembly/AssemblyTestClass.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using LinqToLdap.Mapping; 3 | 4 | namespace LinqToLdap.Tests.ClassMapAssembly 5 | { 6 | public struct SomeStruct 7 | { 8 | } 9 | 10 | public interface ISomethingToThrowOff 11 | { 12 | } 13 | 14 | public class AssemblyTestClass 15 | { 16 | public string Property1 { get; set; } 17 | } 18 | 19 | [DirectorySchema("context", ObjectClasses = new[] { "AssenblyTestClass2" })] 20 | public class AssenblyTestClass2 21 | { 22 | [DirectoryAttribute] 23 | public string Property1 { get; set; } 24 | } 25 | 26 | public class AssemblyTestClassSub : AssemblyTestClass 27 | { 28 | public string Property2 { get; set; } 29 | } 30 | 31 | public class AssemblyTestClassMap : ClassMap 32 | { 33 | public override IClassMap PerformMapping(string namingContext = null, string objectCategory = null, 34 | bool includeObjectCategory = true, IEnumerable objectClasses = null, bool includeObjectClasses = true) 35 | { 36 | NamingContext("name"); 37 | 38 | ObjectClasses(new[] { GetType().Name }); 39 | 40 | Map(p => p.Property1); 41 | 42 | return this; 43 | } 44 | } 45 | 46 | public class AssemblyTestClassSubMap : SubClassMap 47 | { 48 | public AssemblyTestClassSubMap() 49 | : base(new AssemblyTestClassMap()) 50 | { 51 | } 52 | 53 | public override IClassMap PerformMapping(string namingContext = null, string objectCategory = null, bool includeObjectCategory = true, IEnumerable objectClasses = null, bool includeObjectClasses = true) 54 | { 55 | NamingContext("name2"); 56 | 57 | ObjectClasses(new[] { GetType().Name }); 58 | 59 | Map(p => p.Property2); 60 | 61 | return this; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /LinqToLdap/Collections/SafeList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | 5 | namespace LinqToLdap.Collections 6 | { 7 | internal class SafeList 8 | { 9 | private ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); 10 | private List _internal = new List(); 11 | 12 | ~SafeList() 13 | { 14 | var locker = _locker; 15 | if (locker != null) 16 | { 17 | try 18 | { 19 | locker.Dispose(); 20 | } 21 | catch 22 | { 23 | } 24 | 25 | _locker = locker = null; 26 | } 27 | var list = _internal; 28 | if (list != null) 29 | { 30 | list.Clear(); 31 | _internal = list = null; 32 | } 33 | } 34 | 35 | public void Add(T item) 36 | { 37 | _locker.EnterWriteLock(); 38 | try 39 | { 40 | _internal.Add(item); 41 | } 42 | finally 43 | { 44 | _locker.ExitWriteLock(); 45 | } 46 | } 47 | 48 | public List OfType() 49 | { 50 | List values; 51 | _locker.EnterReadLock(); 52 | try 53 | { 54 | values = _internal.OfType().ToList(); 55 | } 56 | finally 57 | { 58 | _locker.ExitReadLock(); 59 | } 60 | 61 | return values; 62 | } 63 | 64 | public List ToList() 65 | { 66 | List values; 67 | _locker.EnterReadLock(); 68 | try 69 | { 70 | values = _internal.ToList(); 71 | } 72 | finally 73 | { 74 | _locker.ExitReadLock(); 75 | } 76 | return values; 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/IQueryCommand.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Logging; 2 | using System.DirectoryServices.Protocols; 3 | 4 | namespace LinqToLdap.QueryCommands 5 | { 6 | /// 7 | /// Interface for a command that executes a query against the directory. 8 | /// 9 | public interface IQueryCommand 10 | { 11 | /// 12 | /// Executes a query against the . 13 | /// 14 | /// Connection to use. 15 | /// Search depth. 16 | /// Indicates if paging is enabled. Defaults to true 17 | /// Log for writing information. 18 | /// The max server page size. Defaults to 500 19 | /// DistinguishedName for the 20 | /// 21 | object Execute(DirectoryConnection connection, SearchScope scope, int maxPageSize, bool pagingEnabled, ILinqToLdapLogger log = null, string namingContext = null); 22 | 23 | #if !NET35 && !NET40 24 | 25 | /// 26 | /// Executes a query against the . 27 | /// 28 | /// Connection to use. 29 | /// Search depth. 30 | /// Indicates if paging is enabled. Defaults to true 31 | /// Log for writing information. 32 | /// The max server page size. Defaults to 500 33 | /// DistinguishedName for the 34 | /// 35 | System.Threading.Tasks.Task ExecuteAsync(LdapConnection connection, SearchScope scope, int maxPageSize, bool pagingEnabled, ILinqToLdapLogger log = null, string namingContext = null); 36 | 37 | #endif 38 | } 39 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/GuidPropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.DirectoryServices.Protocols; 3 | 4 | namespace LinqToLdap.Mapping.PropertyMappings 5 | { 6 | internal class GuidPropertyMapping : PropertyMappingGeneric where T : class 7 | { 8 | public GuidPropertyMapping(PropertyMappingArguments arguments) : base(arguments) 9 | { 10 | } 11 | 12 | public override string FormatValueToFilter(object value) 13 | { 14 | return value != null 15 | ? ((Guid)value).ToStringOctet() 16 | : null; 17 | } 18 | 19 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 20 | { 21 | var modification = new DirectoryAttributeModification 22 | { 23 | Name = AttributeName, 24 | Operation = DirectoryAttributeOperation.Replace 25 | }; 26 | var value = (byte[])GetValueForDirectory(instance); 27 | 28 | if (value != null) 29 | { 30 | modification.Add(value); 31 | } 32 | 33 | return modification; 34 | } 35 | 36 | public override object GetValueForDirectory(object instance) 37 | { 38 | var value = GetValue(instance); 39 | 40 | return value == null ? null : ((Guid)value).ToByteArray(); 41 | } 42 | 43 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 44 | { 45 | if (value != null) 46 | { 47 | var bytes = value.GetValues(typeof(byte[]))[0] as byte[]; 48 | if (bytes == null) ThrowMappingException(dn); 49 | 50 | try 51 | { 52 | return new Guid(bytes); 53 | } 54 | catch (Exception ex) 55 | { 56 | ThrowMappingException(value, dn, ex); 57 | } 58 | } 59 | 60 | AssertNullable(dn); 61 | 62 | return null; 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/Collections/SearchResponseEnumeratorIntegrationTest.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | using LinqToLdap.Collections; 3 | using LinqToLdap.Tests.TestSupport.ExtensionMethods; 4 | using LinqToLdap.Transformers; 5 | using Moq; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | using SharpTestsEx; 8 | 9 | namespace LinqToLdap.Tests.Collections 10 | { 11 | [TestClass] 12 | public class SearchResponseEnumeratorIntegrationTest 13 | { 14 | private Mock _resultTransformer; 15 | 16 | [TestInitialize] 17 | public void SetUp() 18 | { 19 | _resultTransformer = new Mock(); 20 | } 21 | 22 | [TestMethod] 23 | public void Current_MockTransformer_CallsTransformOnMockTransformer() 24 | { 25 | //prepare 26 | var fake = new object(); 27 | 28 | var searchResultAttributesCollection = 29 | typeof(SearchResultAttributeCollection).Create(); 30 | 31 | var searchResultsEntry = 32 | typeof(SearchResultEntry).Create( 33 | new object[] { "dn", searchResultAttributesCollection }); 34 | 35 | var searchResultEntryCollection = 36 | typeof(SearchResultEntryCollection).Create(); 37 | 38 | searchResultEntryCollection 39 | .Call("Add", new object[] { searchResultsEntry }); 40 | 41 | _resultTransformer.Setup(t => t.Transform(searchResultsEntry)) 42 | .Returns(fake); 43 | 44 | 45 | //act 46 | var enumerator = 47 | new SearchResponseEnumerable(searchResultEntryCollection, 48 | _resultTransformer.Object).GetEnumerator(); 49 | 50 | //assert 51 | enumerator.MoveNext().Should().Be.True(); 52 | enumerator.Current.Should().Be.SameInstanceAs(fake); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /LinqToLdap/Collections/LdapPage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace LinqToLdap.Collections 4 | { 5 | /// 6 | /// Represents a page of items 7 | /// 8 | /// The type of items 9 | public class LdapPage : List, ILdapPage 10 | { 11 | /// 12 | /// Creates a a paged collection. 13 | /// 14 | /// The size of the page 15 | /// The cookie for the next page 16 | /// The items for the page 17 | /// Filter used to produce this page 18 | public LdapPage(int pageSize, byte[] nextPage, IEnumerable enumerable, string filter) 19 | : this(pageSize, nextPage, enumerable) 20 | { 21 | Filter = filter; 22 | } 23 | 24 | /// 25 | /// Creates a a paged collection. 26 | /// 27 | /// The size of the page 28 | /// The cookie for the next page 29 | /// The items for the page 30 | public LdapPage(int pageSize, byte[] nextPage, IEnumerable page) 31 | : base(page) 32 | { 33 | PageSize = pageSize; 34 | NextPage = nextPage; 35 | 36 | HasNextPage = NextPage != null && NextPage.Length > 0; 37 | } 38 | 39 | /// 40 | /// The size of the page. 41 | /// 42 | public int PageSize { get; private set; } 43 | 44 | /// 45 | /// Indicates if there is another page. 46 | /// 47 | public bool HasNextPage { get; private set; } 48 | 49 | /// 50 | /// The cookie for the next page. 51 | /// 52 | public byte[] NextPage { get; private set; } 53 | 54 | /// 55 | /// The LDAP Filter used to produce this page. 56 | /// 57 | public string Filter { get; private set; } 58 | } 59 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/IClassMap.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Exceptions; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace LinqToLdap.Mapping 6 | { 7 | /// 8 | /// Interface for a class that maps an object to the directory 9 | /// 10 | public interface IClassMap 11 | { 12 | /// 13 | /// The of the class map. 14 | /// 15 | Type Type { get; } 16 | 17 | /// 18 | /// Maps the schema and property information. 19 | /// 20 | /// The object category for the object. 21 | /// 22 | /// Indicates if the object category should be included in all queries. 23 | /// 24 | /// The location of the objects in the directory. 25 | /// The object classes for the object. 26 | /// Indicates if the object classes should be included in all queries. 27 | /// 28 | IClassMap PerformMapping(string namingContext = null, string objectCategory = null, bool includeObjectCategory = true, IEnumerable objectClasses = null, bool includeObjectClasses = true); 29 | 30 | /// 31 | /// Validates the mapping. Should throw a if the mapping is not valid. 32 | /// 33 | /// Thrown if the mapping is invalid. 34 | void Validate(); 35 | 36 | /// 37 | /// Produces a final mapping used for object construction from the directory 38 | /// 39 | /// 40 | IObjectMapping ToObjectMapping(); 41 | 42 | /// 43 | /// Indicates if this class should flatten its hierarchy when mapping. Flattened mappings will include inherited properties, but will not work with queries for subtypes or base types. 44 | /// 45 | bool WithoutSubTypeMapping { get; } 46 | } 47 | } -------------------------------------------------------------------------------- /LinqToLdap/Visitors/Nominator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | 5 | namespace LinqToLdap.Visitors 6 | { 7 | /// 8 | /// Performs bottom-up analysis to determine which nodes can possibly 9 | /// be part of an evaluated sub-tree. 10 | /// 11 | internal class Nominator : ExpressionVisitor 12 | { 13 | private readonly Func _fnCanBeEvaluated; 14 | private HashSet _candidates; 15 | private bool _cannotBeEvaluated; 16 | 17 | public Nominator(Func fnCanBeEvaluated) 18 | { 19 | _fnCanBeEvaluated = fnCanBeEvaluated; 20 | } 21 | 22 | public HashSet Nominate(Expression expression) 23 | { 24 | _candidates = new HashSet(); 25 | Visit(expression); 26 | return _candidates; 27 | } 28 | 29 | private static Expression StripQuotes(Expression e) 30 | { 31 | while (e.NodeType == ExpressionType.Quote) 32 | { 33 | e = ((UnaryExpression)e).Operand; 34 | } 35 | return e; 36 | } 37 | 38 | //protected override Expression VisitUnary(UnaryExpression u) 39 | //{ 40 | // return u.NodeType == ExpressionType.Quote ? StripQuotes(u) : base.VisitUnary(u); 41 | //} 42 | 43 | protected override Expression Visit(Expression expression) 44 | { 45 | if (expression != null) 46 | { 47 | bool saveCannotBeEvaluated = _cannotBeEvaluated; 48 | _cannotBeEvaluated = false; 49 | base.Visit(expression); 50 | if (!_cannotBeEvaluated) 51 | { 52 | if (_fnCanBeEvaluated(expression)) 53 | { 54 | _candidates.Add(expression); 55 | } 56 | else 57 | { 58 | _cannotBeEvaluated = true; 59 | } 60 | } 61 | _cannotBeEvaluated |= saveCannotBeEvaluated; 62 | } 63 | return expression; 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/X509Certificate2PropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.DirectoryServices.Protocols; 3 | using System.Security.Cryptography.X509Certificates; 4 | 5 | namespace LinqToLdap.Mapping.PropertyMappings 6 | { 7 | internal class X509Certificate2PropertyMapping : PropertyMappingGeneric where T : class 8 | { 9 | public X509Certificate2PropertyMapping(PropertyMappingArguments arguments) : base(arguments) 10 | { 11 | } 12 | 13 | public override string FormatValueToFilter(object value) 14 | { 15 | return value != null 16 | ? ((X509Certificate)value).GetRawCertData().ToStringOctet() 17 | : null; 18 | } 19 | 20 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 21 | { 22 | var modification = new DirectoryAttributeModification 23 | { 24 | Name = AttributeName, 25 | Operation = DirectoryAttributeOperation.Replace 26 | }; 27 | var value = (byte[])GetValueForDirectory(instance); 28 | 29 | if (value != null) 30 | { 31 | modification.Add(value); 32 | } 33 | 34 | return modification; 35 | } 36 | 37 | public override object GetValueForDirectory(object instance) 38 | { 39 | var value = GetValue(instance); 40 | 41 | return value == null ? null : ((X509Certificate)value).GetRawCertData(); 42 | } 43 | 44 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 45 | { 46 | if (value != null) 47 | { 48 | var bytes = value.GetValues(typeof(byte[]))[0] as byte[]; 49 | if (bytes == null) ThrowMappingException(dn); 50 | 51 | try 52 | { 53 | return new X509Certificate2(bytes); 54 | } 55 | catch (Exception ex) 56 | { 57 | ThrowMappingException(value, dn, ex); 58 | } 59 | } 60 | 61 | AssertNullable(dn); 62 | 63 | return null; 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/DirectoryObjectBase.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Collections; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | 5 | namespace LinqToLdap.Mapping 6 | { 7 | /// 8 | /// Base class for updatable directory objects. 9 | /// 10 | public abstract class DirectoryObjectBase : IDirectoryObject 11 | { 12 | /// 13 | /// Method for identifying changes. Simplifies implementing manually. 14 | /// 15 | public static IEnumerable GetChanges(object instance, IObjectMapping mapping, OriginalValuesCollection collection) 16 | { 17 | if (collection == null || collection.Count == 0) 18 | { 19 | foreach (var property in mapping.GetPropertyMappingsForUpdate()) 20 | { 21 | yield return property.GetDirectoryAttributeModification(instance); 22 | } 23 | } 24 | else 25 | { 26 | foreach (var updateablePropertyMapping in mapping.GetPropertyMappingsForUpdate()) 27 | { 28 | var propertyName = updateablePropertyMapping.PropertyName; 29 | object value = collection[propertyName]; 30 | DirectoryAttributeModification modification; 31 | if (!updateablePropertyMapping.IsEqual(instance, value, out modification)) 32 | { 33 | yield return modification; 34 | } 35 | } 36 | } 37 | } 38 | 39 | /// 40 | /// Gets the changes to send to the directory. 41 | /// 42 | /// The mapping for the object. 43 | /// 44 | public IEnumerable GetChanges(IObjectMapping mapping) 45 | { 46 | return GetChanges(this, mapping, OriginalValues); 47 | } 48 | 49 | /// 50 | /// The original property values loaded form the directory for this object. 51 | /// 52 | public OriginalValuesCollection OriginalValues { get; set; } 53 | } 54 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/DirectorySchemaAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace LinqToLdap.Mapping 5 | { 6 | /// 7 | /// Maps an object to a naming context in the directory 8 | /// 9 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 10 | public class DirectorySchemaAttribute : Attribute 11 | { 12 | /// 13 | /// Maps the object to the naming context 14 | /// 15 | /// The naming context 16 | public DirectorySchemaAttribute(string namingContext) 17 | { 18 | NamingContext = namingContext; 19 | } 20 | 21 | /// 22 | /// Mapped object classes 23 | /// 24 | public string[] ObjectClasses { get; set; } 25 | 26 | /// 27 | /// Mapped object class 28 | /// 29 | public string ObjectClass 30 | { 31 | get { return ObjectClasses?.FirstOrDefault(); } 32 | set 33 | { 34 | if (!value.IsNullOrEmpty()) 35 | { 36 | ObjectClasses = new[] { value }; 37 | } 38 | } 39 | } 40 | 41 | /// 42 | /// Mapped naming context 43 | /// 44 | public string NamingContext { get; private set; } 45 | 46 | /// 47 | /// Mapped object category 48 | /// 49 | public string ObjectCategory { get; set; } 50 | 51 | /// 52 | /// Indicates if the object category should be included in filters. 53 | /// 54 | public bool IncludeObjectCategory { get; set; } = true; 55 | 56 | /// 57 | /// Indicates if the object classes should be included in filters. 58 | /// 59 | public bool IncludeObjectClasses { get; set; } = true; 60 | 61 | /// 62 | /// Indicates if this class should flatten its hierarchy when mapping. Flattened mappings will include inherited properties, but will not work with queries for subtypes or base types. 63 | /// 64 | public bool WithoutSubTypeMapping { get; set; } 65 | } 66 | } -------------------------------------------------------------------------------- /LinqToLdap/LinqToLdap.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1;netstandard2.0;net35;net40;net45 5 | true 6 | true 7 | LinqToLdap 8 | 5.0.0 9 | Alan Hatter 10 | Alan Hatter 11 | LINQ provider built on top of System.DirectoryServices.Protocols for querying and updating LDAP servers. 12 | LINQ to LDAP 13 | LINQ LDAP AD active directory openldap tivoli S.DS.P SDSP 14 | linqtoldap_logo_small.png 15 | git 16 | https://github.com/madhatter22/LinqToLdap.git 17 | https://github.com/madhatter22/LinqToLdap 18 | https://github.com/madhatter22/LinqToLdap/releases/tag/5.0.0 19 | false 20 | en-US 21 | true 22 | false 23 | linqtoldap.snk 24 | MIT 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /LinqToLdap.Tests.PopulateDirectory/LdsUser.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Mapping; 2 | using System; 3 | using System.Collections.ObjectModel; 4 | using System.Security.Principal; 5 | 6 | namespace LinqToLdap.Tests.PopulateDirectory 7 | { 8 | [DirectorySchema("", ObjectClass = "User")] 9 | public class LdsUser 10 | 11 | { 12 | [DistinguishedName] 13 | public string DistinguishedName { get; set; } 14 | 15 | [DirectoryAttribute] 16 | public string Manager { get; set; } 17 | 18 | [DirectoryAttribute("badpwdcount", true)] 19 | public int BadPasswordCount { get; set; } 20 | 21 | public Collection Employees { get; set; } 22 | 23 | [DirectoryAttribute] 24 | public string Title { get; set; } 25 | 26 | [DirectoryAttribute] 27 | public string PostalCode { get; set; } 28 | 29 | [DirectoryAttribute("cn", true)] 30 | public string CommonName { get; set; } 31 | 32 | [DirectoryAttribute(true)] 33 | public DateTime? WhenCreated { get; set; } 34 | 35 | [DirectoryAttribute("givenname")] 36 | public string FirstName { get; set; } 37 | 38 | [DirectoryAttribute("objectguid", true)] 39 | public Guid Guid { get; set; } 40 | 41 | [DirectoryAttribute("l")] 42 | public string City { get; set; } 43 | 44 | [DirectoryAttribute("usnchanged", true)] 45 | public int Version { get; set; } 46 | 47 | [DirectoryAttribute("c")] 48 | public string Country { get; set; } 49 | 50 | [DirectoryAttribute("whenchanged", true)] 51 | public DateTime? LastChanged { get; set; } 52 | 53 | [DirectoryAttribute("objectsid", true)] 54 | public SecurityIdentifier Sid { get; set; } 55 | 56 | [DirectoryAttribute("employeeid")] 57 | public long EmployeeId { get; set; } 58 | 59 | [DirectoryAttribute("telephonenumber")] 60 | public string PhoneNumber { get; set; } 61 | 62 | [DirectoryAttribute] 63 | public string Street { get; set; } 64 | 65 | [DirectoryAttribute] 66 | public string Comment { get; set; } 67 | 68 | [DirectoryAttribute] 69 | public string Name { get; set; } 70 | 71 | [DirectoryAttribute("sn")] 72 | public string LastName { get; set; } 73 | 74 | [DirectoryAttribute("notes")] 75 | public string Comments { get; set; } 76 | } 77 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/SecurityIdentifierPropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | using System.Security.Principal; 3 | 4 | namespace LinqToLdap.Mapping.PropertyMappings 5 | { 6 | internal class SecurityIdentifierPropertyMapping : PropertyMappingGeneric where T : class 7 | { 8 | public SecurityIdentifierPropertyMapping(PropertyMappingArguments arguments) : base(arguments) 9 | { 10 | } 11 | 12 | public override string FormatValueToFilter(object value) 13 | { 14 | if (value != null) 15 | { 16 | var identifier = value as SecurityIdentifier; 17 | var binary = new byte[identifier.BinaryLength]; 18 | 19 | identifier.GetBinaryForm(binary, 0); 20 | 21 | return binary.ToStringOctet(); 22 | } 23 | 24 | return null; 25 | } 26 | 27 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 28 | { 29 | var modification = new DirectoryAttributeModification 30 | { 31 | Name = AttributeName, 32 | Operation = DirectoryAttributeOperation.Replace 33 | }; 34 | var value = (byte[])GetValueForDirectory(instance); 35 | 36 | if (value != null) 37 | { 38 | modification.Add(value); 39 | } 40 | 41 | return modification; 42 | } 43 | 44 | public override object GetValueForDirectory(object instance) 45 | { 46 | var value = GetValue(instance); 47 | if (value == null) return value; 48 | 49 | var identifier = value as SecurityIdentifier; 50 | var binary = new byte[identifier.BinaryLength]; 51 | 52 | identifier.GetBinaryForm(binary, 0); 53 | 54 | return binary; 55 | } 56 | 57 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 58 | { 59 | if (value != null) 60 | { 61 | var bytes = value.GetValues(typeof(byte[]))[0] as byte[]; 62 | if (bytes == null) ThrowMappingException(dn); 63 | 64 | return new SecurityIdentifier(bytes, 0); 65 | } 66 | 67 | AssertNullable(dn); 68 | 69 | return null; 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /LinqToLdap/EventListeners/IEventListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | using System.Text; 5 | 6 | namespace LinqToLdap.EventListeners 7 | { 8 | /// 9 | /// Interface for events available for subscription notifications. 10 | /// 11 | public interface IEventListener 12 | { 13 | } 14 | 15 | /// 16 | /// Interface for update events. 17 | /// 18 | public interface IUpdateEventListener : IEventListener 19 | { 20 | } 21 | 22 | /// 23 | /// Interface for add events. 24 | /// 25 | public interface IAddEventListener : IEventListener 26 | { 27 | } 28 | 29 | /// 30 | /// Interface for delete events. 31 | /// 32 | public interface IDeleteEventListener : IEventListener 33 | { 34 | } 35 | 36 | /// 37 | /// Interface for events available for subscription notifications. 38 | /// 39 | /// The instance for the event. 40 | /// The partially populated request to be sent to the directory. 41 | public interface IPreEventListener : IEventListener where TRequest : DirectoryRequest where TObject : class 42 | { 43 | /// 44 | /// Executes when the event is raised. 45 | /// 46 | /// The arguments. 47 | void Notify(ListenerPreArgs preArgs); 48 | } 49 | 50 | /// 51 | /// Interface for events available for subscription notifications. 52 | /// 53 | /// The instance for the event. 54 | /// /// The request sent to the directory 55 | /// The response from the directory. 56 | public interface IPostEventListener : IEventListener 57 | where TResponse : DirectoryResponse 58 | where TRequest : DirectoryRequest 59 | where TObject : class 60 | { 61 | /// 62 | /// Executes when the event is raised. 63 | /// 64 | /// The arguments. 65 | void Notify(ListenerPostArgs preArgs); 66 | } 67 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappingBuilders/IDateTimePropertyMappingBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LinqToLdap.Mapping.PropertyMappingBuilders 4 | { 5 | /// 6 | /// Interface for a DateTimePropertyMappingBuilder. 7 | /// 8 | /// The type of the owner of the property 9 | /// The type of the property 10 | public interface IDateTimePropertyMappingBuilder : IDirectoryValueConversionMapper, TProperty>, 11 | IDirectoryToConversion, TProperty>, 12 | IInstanceValueConversionMapper, TProperty>, 13 | IInstanceToConversion> where T : class 14 | { 15 | /// 16 | /// Specify an attribute name for a mapped property. 17 | /// 18 | /// Attribute name in the directory 19 | /// 20 | IDateTimePropertyMappingBuilder Named(string attributeName); 21 | 22 | /// 23 | /// Specify the format of the DateTime in the directory. Use null if the DateTime is stored in file time. 24 | /// Default format is yyyyMMddHHmmss.0Z. If the property is not a then this setting will be ignored. 25 | /// 26 | /// 27 | /// Common formats 28 | /// 29 | /// Active Directory: yyyyMMddHHmmss.0Z 30 | /// 31 | /// 32 | /// eDirectory: yyyyMMddHHmmssZ or yyyyMMddHHmmss00Z 33 | /// 34 | /// 35 | /// The format of the DateTime in the directory. 36 | /// 37 | IDateTimePropertyMappingBuilder DateTimeFormat(string format); 38 | 39 | /// 40 | /// Indicates if the property should always be read only. 41 | /// 42 | /// 43 | IDateTimePropertyMappingBuilder ReadOnly(); 44 | 45 | /// 46 | /// Configures the read only behavior. 47 | /// 48 | /// 49 | IDateTimePropertyMappingBuilder ReadOnly(ReadOnly readOnly); 50 | } 51 | } -------------------------------------------------------------------------------- /LinqToLdap/EventListeners/ListenerPreArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.DirectoryServices.Protocols; 3 | 4 | namespace LinqToLdap.EventListeners 5 | { 6 | /// 7 | /// Arguments passed for s. The arguments used by this class are s. 8 | /// 9 | /// The instance for the event. 10 | /// The partially populated request to be sent to the directory. 11 | public class ListenerPreArgs where TRequest : DirectoryRequest where TObject : class 12 | { 13 | #if (NET35 || NET40) 14 | private readonly WeakReference _connection; 15 | private readonly WeakReference _entry; 16 | #else 17 | private readonly WeakReference _connection; 18 | private readonly WeakReference _entry; 19 | #endif 20 | 21 | internal ListenerPreArgs(TObject entry, TRequest request, LdapConnection connection) 22 | { 23 | #if (NET35 || NET40) 24 | _entry = new WeakReference(entry); 25 | _connection = new WeakReference(connection); 26 | #else 27 | _entry = new WeakReference(entry); 28 | _connection = new WeakReference(connection); 29 | #endif 30 | Request = request; 31 | } 32 | 33 | /// 34 | /// The entry. 35 | /// 36 | /// 37 | public TObject Entry 38 | { 39 | get 40 | { 41 | #if (NET35 || NET40) 42 | return _entry.Target as TObject; 43 | #else 44 | TObject target; 45 | return _entry.TryGetTarget(out target) ? target : default(TObject); 46 | #endif 47 | } 48 | } 49 | 50 | /// 51 | /// The . 52 | /// 53 | public TRequest Request { get; private set; } 54 | 55 | /// 56 | /// The connection that will be used when sending the . 57 | /// 58 | public LdapConnection Connection 59 | { 60 | get 61 | { 62 | #if (NET35 || NET40) 63 | return _connection.Target as LdapConnection; 64 | #else 65 | LdapConnection target; 66 | return _connection.TryGetTarget(out target) ? target : null; 67 | #endif 68 | } 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/EnumPropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.DirectoryServices.Protocols; 3 | 4 | namespace LinqToLdap.Mapping.PropertyMappings 5 | { 6 | internal class EnumPropertyMapping : PropertyMappingGeneric where T : class 7 | { 8 | private readonly bool _isStoredAsInt; 9 | 10 | public EnumPropertyMapping(PropertyMappingArguments arguments, bool isStoredAsInt) : base(arguments) 11 | { 12 | _isStoredAsInt = isStoredAsInt; 13 | } 14 | 15 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 16 | { 17 | string str; 18 | if (value != null && value.Count > 0 && (str = value[0] as string) != null && !str.IsNullOrEmpty()) 19 | { 20 | try 21 | { 22 | var en = Enum.Parse(UnderlyingType, str); 23 | 24 | return en; 25 | } 26 | catch (Exception ex) 27 | { 28 | ThrowMappingException(str, dn, ex); 29 | } 30 | } 31 | 32 | AssertNullable(dn); 33 | 34 | return null; 35 | } 36 | 37 | public override string FormatValueToFilter(object value) 38 | { 39 | if (_isStoredAsInt) 40 | { 41 | return ((int)value).ToString(); 42 | } 43 | if (value is int) 44 | { 45 | return Enum.GetName(UnderlyingType, value); 46 | } 47 | return value.ToString(); 48 | } 49 | 50 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 51 | { 52 | var modification = new DirectoryAttributeModification 53 | { 54 | Name = AttributeName, 55 | Operation = DirectoryAttributeOperation.Replace 56 | }; 57 | var value = (string)GetValueForDirectory(instance); 58 | 59 | if (value != null) 60 | { 61 | modification.Add(value); 62 | } 63 | 64 | return modification; 65 | } 66 | 67 | public override object GetValueForDirectory(object instance) 68 | { 69 | var value = GetValue(instance); 70 | 71 | if (value == null) return value; 72 | 73 | return _isStoredAsInt 74 | ? ((int)value).ToString() 75 | : value.ToString(); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/SubClassMap.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Exceptions; 2 | using System; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | namespace LinqToLdap.Mapping 7 | { 8 | /// 9 | /// Defines a mapping for a directory entry. Derive from this class to create a mapping, 10 | /// and use the constructor to control how your object is queryed. 11 | /// 12 | /// 13 | /// public class UserMap : SubClassMap<User> 14 | /// { 15 | /// public UserMap() : base(new SuperUserMap()) 16 | /// { 17 | /// Map(x => x.Name) 18 | /// .Named("displayname"); 19 | /// Map(x => x.Age); 20 | /// } 21 | /// } 22 | /// 23 | /// Type to map 24 | /// Super type for . 25 | public abstract class SubClassMap : ClassMap where T : class, TSuper where TSuper : class 26 | { 27 | /// 28 | /// Adds the properties from the parent mapping to the sub class. 29 | /// 30 | /// 31 | protected SubClassMap(ClassMap parentMapping) 32 | { 33 | var mapping = (ClassMap)parentMapping.PerformMapping(); 34 | foreach (var property in mapping.PropertyMappings) 35 | { 36 | PropertyMappings.Add(property); 37 | } 38 | } 39 | 40 | /// 41 | /// Removes the mapping for a property. 42 | /// 43 | /// The type of the property 44 | /// The property to remove 45 | protected void Ignore(Expression> property) 46 | { 47 | if (property == null) throw new ArgumentNullException("property"); 48 | 49 | var memberExpression = new Visitors.MemberVisitor().GetMember(property.Body) as MemberExpression; 50 | 51 | if (memberExpression == null) 52 | throw new ArgumentException("Expected MemberAccess expression but was " + property.Body.NodeType); 53 | 54 | var mappedProperty = PropertyMappings.FirstOrDefault(p => p.PropertyName == memberExpression.Member.Name); 55 | if (mappedProperty == null) 56 | { 57 | throw new MappingException(memberExpression.Member.Name + " has not been mapped."); 58 | } 59 | 60 | PropertyMappings.Remove(mappedProperty); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/StandardObjectMapping.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Helpers; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace LinqToLdap.Mapping 7 | { 8 | internal class StandardObjectMapping : ObjectMapping where T : class 9 | { 10 | private readonly Ctor _constructor; 11 | 12 | #if NET35 13 | private readonly LinqToLdap.Collections.SafeDictionary _fullObjectClassMappings = new LinqToLdap.Collections.SafeDictionary(); 14 | #else 15 | 16 | private readonly System.Collections.Concurrent.ConcurrentDictionary _fullObjectClassMappings = 17 | new System.Collections.Concurrent.ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); 18 | 19 | #endif 20 | 21 | public StandardObjectMapping(string namingContext, 22 | IEnumerable propertyMappings, string objectCategory, bool includeObjectCategory, IEnumerable objectClass, bool includeObjectClasses) 23 | : base(namingContext, propertyMappings, objectCategory, includeObjectCategory, objectClass, includeObjectClasses) 24 | { 25 | _constructor = DelegateBuilder.BuildCtor(); 26 | } 27 | 28 | public override bool IsForAnonymousType => false; 29 | public override Type Type => typeof(T); 30 | 31 | public override object Create(object[] parameters = null, object[] objectClasses = null) 32 | { 33 | if (HasSubTypeMappings && objectClasses != null) 34 | { 35 | #if NET35 36 | string joinedObjectClasses = string.Join(" ", objectClasses.Cast().ToArray()); 37 | #else 38 | string joinedObjectClasses = string.Join(" ", objectClasses); 39 | #endif 40 | 41 | if (_fullObjectClassMappings.TryGetValue(joinedObjectClasses, out IObjectMapping mapping)) 42 | { 43 | return mapping.Create(); 44 | } 45 | //Reverse the object classes in order of most specific to least specific. 46 | //AD and openLDAP seem to retrieve the classes in order of least to most specific so I assume it's the standard. 47 | if (objectClasses.Cast().Reverse().Any(objectClass => SubTypeMappingsObjectClassDictionary.TryGetValue(objectClass, out mapping))) 48 | { 49 | _fullObjectClassMappings.TryAdd(joinedObjectClasses, mapping); 50 | return mapping.Create(); 51 | } 52 | } 53 | return _constructor(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/Options/QueryCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Collections; 2 | using LinqToLdap.Helpers; 3 | using LinqToLdap.Transformers; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.DirectoryServices.Protocols; 7 | 8 | namespace LinqToLdap.QueryCommands.Options 9 | { 10 | internal abstract class QueryCommandOptions : IQueryCommandOptions 11 | { 12 | protected QueryCommandOptions(SelectProjection selectProjection) 13 | { 14 | SelectProjection = selectProjection; 15 | AttributesToLoad = selectProjection.SelectedProperties; 16 | } 17 | 18 | protected QueryCommandOptions(IDictionary queriedAttributes) 19 | { 20 | AttributesToLoad = queriedAttributes; 21 | } 22 | 23 | public IDictionary AttributesToLoad { get; private set; } 24 | 25 | public string Filter { get; set; } 26 | 27 | public bool IsLongCount { get; set; } 28 | 29 | public bool WithoutPaging { get; set; } 30 | 31 | public IPagingOptions PagingOptions { get; set; } 32 | 33 | protected SelectProjection SelectProjection { get; private set; } 34 | 35 | public ISortingOptions SortingOptions { get; set; } 36 | 37 | public IEnumerable Controls { get; set; } 38 | 39 | public int? PageSize { get; set; } 40 | 41 | public int? TakeSize { get; set; } 42 | 43 | public int? SkipSize { get; set; } 44 | 45 | public bool YieldNoResults { get; set; } 46 | 47 | public PartialResultProcessing AsyncProcessing { get; set; } 48 | 49 | public object GetEnumerator(SearchResultEntryCollection results) 50 | { 51 | return ObjectActivator.CreateGenericInstance(typeof(SearchResponseEnumerable<>), GetEnumeratorReturnType(), 52 | new object[] { results, GetTransformer() }, "1"); 53 | } 54 | 55 | public object GetEnumerator() 56 | { 57 | return ObjectActivator.CreateGenericInstance(typeof(SearchResponseEnumerable<>), GetEnumeratorReturnType(), 58 | new object[] { null, GetTransformer() }, "1"); 59 | } 60 | 61 | public object GetEnumerator(List results) 62 | { 63 | return ObjectActivator.CreateGenericInstance(typeof(SearchResponseEnumerable<>), GetEnumeratorReturnType(), 64 | new object[] { GetTransformer(), results }, "2"); 65 | } 66 | 67 | public abstract Type GetEnumeratorReturnType(); 68 | 69 | public abstract IResultTransformer GetTransformer(); 70 | } 71 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/QueryCommands/GetRequestCommandTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Xml; 7 | using LinqToLdap.Mapping; 8 | using LinqToLdap.QueryCommands; 9 | using LinqToLdap.QueryCommands.Options; 10 | using LinqToLdap.Tests.TestSupport; 11 | using LinqToLdap.Tests.TestSupport.ExtensionMethods; 12 | using Moq; 13 | using Microsoft.VisualStudio.TestTools.UnitTesting; 14 | using SharpTestsEx; 15 | 16 | namespace LinqToLdap.Tests.QueryCommands 17 | { 18 | [TestClass] 19 | public class GetRequestCommandTest 20 | { 21 | [TestMethod] 22 | public void Execute_ReturnsRequest() 23 | { 24 | //prepare 25 | var sortingOptions = new SortingOptions(); 26 | sortingOptions.AddSort("att1", true); 27 | var options = new Mock(); 28 | options.SetupGet(o => o.Filter) 29 | .Returns("x=1"); 30 | options.SetupGet(o => o.AttributesToLoad) 31 | .Returns(new Dictionary {{"Property1", "prop1"}, {"Property2", "prop2"}}); 32 | options.SetupGet(o => o.SortingOptions) 33 | .Returns(sortingOptions); 34 | options.SetupGet(o => o.Controls) 35 | .Returns(new DirectoryControl[] {new AsqRequestControl("att")}); 36 | options.SetupGet(o => o.TakeSize) 37 | .Returns(2); 38 | var connection = new MockLdapConnection(); 39 | var mapping = new Mock(); 40 | mapping.Setup(m => m.NamingContext) 41 | .Returns("nm"); 42 | var command = new GetRequestCommand(options.Object, mapping.Object); 43 | 44 | //act 45 | var result = command.Execute(connection, SearchScope.Subtree, 1, true).As(); 46 | 47 | //assert 48 | result.Filter.Should().Be.EqualTo("x=1"); 49 | result.Controls[0].As().AttributeName.Should().Be.EqualTo("att"); 50 | result.Controls[1].As().SortKeys[0].AttributeName.Should().Be.EqualTo("att1"); 51 | result.Controls[1].As().SortKeys[0].ReverseOrder.Should().Be.True(); 52 | result.Controls[2].As().PageSize.Should().Be.EqualTo(2); 53 | result.Attributes[0].Should().Be.EqualTo("prop1"); 54 | result.Attributes[1].Should().Be.EqualTo("prop2"); 55 | result.Scope.Should().Be.EqualTo(SearchScope.Subtree); 56 | 57 | result.DistinguishedName.Should().Be.EqualTo("nm"); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /LinqToLdap/Collections/SearchResponseEnumerable.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Transformers; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.DirectoryServices.Protocols; 5 | 6 | namespace LinqToLdap.Collections 7 | { 8 | internal class SearchResponseEnumerable : IEnumerable 9 | { 10 | private SearchResultEntryCollection _entries; 11 | private List _enumerableEntries; 12 | private IResultTransformer _resultTransformer; 13 | private readonly int _count; 14 | 15 | public SearchResponseEnumerable(SearchResultEntryCollection entries, IResultTransformer resultTransformer) 16 | { 17 | _resultTransformer = resultTransformer; 18 | _entries = entries; 19 | _count = entries != null ? entries.Count : 0; 20 | } 21 | 22 | public SearchResponseEnumerable(IResultTransformer resultTransformer, List entries) 23 | { 24 | _resultTransformer = resultTransformer; 25 | _enumerableEntries = entries; 26 | _count = _enumerableEntries != null ? _enumerableEntries.Count : 0; 27 | } 28 | 29 | IEnumerator IEnumerable.GetEnumerator() 30 | { 31 | if (_count == 0) 32 | { 33 | yield break; 34 | } 35 | 36 | if (_entries != null) 37 | { 38 | foreach (SearchResultEntry entry in _entries) 39 | { 40 | yield return (T)_resultTransformer.Transform(entry); 41 | } 42 | } 43 | else 44 | { 45 | foreach (SearchResultEntry entry in _enumerableEntries) 46 | { 47 | yield return (T)_resultTransformer.Transform(entry); 48 | } 49 | } 50 | } 51 | 52 | public IEnumerator GetEnumerator() 53 | { 54 | if (_count == 0) 55 | { 56 | yield break; 57 | } 58 | 59 | if (_entries != null) 60 | { 61 | foreach (SearchResultEntry entry in _entries) 62 | { 63 | yield return _resultTransformer.Transform(entry); 64 | } 65 | } 66 | else 67 | { 68 | foreach (SearchResultEntry entry in _enumerableEntries) 69 | { 70 | yield return _resultTransformer.Transform(entry); 71 | } 72 | } 73 | } 74 | 75 | public void Dispose() 76 | { 77 | _entries = null; 78 | _enumerableEntries = null; 79 | _resultTransformer = null; 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/ServerObjectMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | 5 | namespace LinqToLdap.Mapping 6 | { 7 | internal class ServerObjectMapping : IObjectMapping 8 | { 9 | public Type Type => GetType(); 10 | 11 | public bool IsForAnonymousType => false; 12 | 13 | public string NamingContext => null; 14 | 15 | public string ObjectCategory => null; 16 | 17 | public bool IncludeObjectCategory => false; 18 | 19 | public IEnumerable ObjectClasses => null; 20 | 21 | public bool HasCatchAllMapping => false; 22 | public bool IncludeObjectClasses => false; 23 | 24 | public bool FlattenHierarchy => true; 25 | 26 | #if (!NET35 && !NET40) 27 | public System.Collections.ObjectModel.ReadOnlyDictionary Properties => new System.Collections.ObjectModel.ReadOnlyDictionary(new Dictionary()); 28 | #else 29 | public Collections.ReadOnlyDictionary Properties => new Collections.ReadOnlyDictionary(new Dictionary()); 30 | #endif 31 | 32 | public IEnumerable GetPropertyMappings() 33 | { 34 | return new List(0); 35 | } 36 | 37 | public IEnumerable GetPropertyMappingsForAdd() 38 | { 39 | return new List(0); 40 | } 41 | 42 | public IEnumerable GetPropertyMappingsForUpdate() 43 | { 44 | return new List(0); 45 | } 46 | 47 | public IPropertyMapping GetPropertyMapping(string name, Type owningType = null) 48 | { 49 | return null; 50 | } 51 | 52 | public IPropertyMapping GetPropertyMappingByAttribute(string name, Type owningType = null) 53 | { 54 | return null; 55 | } 56 | 57 | public IPropertyMapping GetDistinguishedNameMapping() 58 | { 59 | throw new NotSupportedException(); 60 | } 61 | 62 | public IPropertyMapping GetCatchAllMapping() 63 | { 64 | throw new NotSupportedException(); 65 | } 66 | 67 | public object Create(object[] parameters, object[] objectClasses = null) 68 | { 69 | throw new NotSupportedException(); 70 | } 71 | 72 | public ReadOnlyCollection SubTypeMappings => null; 73 | 74 | public void AddSubTypeMapping(IObjectMapping mapping) 75 | { 76 | throw new NotSupportedException(GetType().Name + " does not support SubTypes"); 77 | } 78 | 79 | public bool HasSubTypeMappings => false; 80 | } 81 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/Mapping/SubClassMapTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using LinqToLdap.Mapping; 3 | using LinqToLdap.Tests.TestSupport.ExtensionMethods; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using SharpTestsEx; 6 | 7 | namespace LinqToLdap.Tests.Mapping 8 | { 9 | public class SubTestClass : TestClass 10 | { 11 | public string Property6 { get; set; } 12 | } 13 | 14 | public class SubTestClassMapping : SubClassMap 15 | { 16 | public SubTestClassMapping() : base(new TestClassMapValid()) 17 | { 18 | } 19 | 20 | public override IClassMap PerformMapping(string namingContext = null, string objectCategory = null, bool includeObjectCategory = true, IEnumerable objectClasses = null, bool includeObjectClasses = true) 21 | { 22 | NamingContext(namingContext); 23 | ObjectClasses(objectClasses); 24 | ObjectCategory(objectCategory); 25 | 26 | Ignore(x => x.Property1); 27 | Ignore(x => x.Property5); 28 | Map(x => x.Property5).Named("ou").ReadOnly(); 29 | Map(x => x.Property6); 30 | 31 | return this; 32 | } 33 | } 34 | 35 | [TestClass] 36 | public class SubClassMapTest 37 | { 38 | [TestMethod] 39 | public void Map_ValidMappingWithIgnore_HasCorrectMappedInformation() 40 | { 41 | //act 42 | var mapping = new SubTestClassMapping().PerformMapping("subcontainer", objectCategory: "subcategory", objectClasses: new[] { "subclass" }) 43 | .CastTo(); 44 | 45 | //assert 46 | mapping.FieldValueEx("_namingContext").Should().Be.EqualTo("subcontainer"); 47 | mapping.FieldValueEx("_objectCategory").Should().Be.EqualTo("subcategory"); 48 | mapping.FieldValueEx>("_objectClass").Should().Be.Equals(new[] { "subclass" }); 49 | 50 | var propertyMappings = mapping.PropertyMappings; 51 | propertyMappings.Should().Have.Count.EqualTo(5); 52 | 53 | var first = propertyMappings[0].As(); 54 | var second = propertyMappings[3].As(); 55 | var third = propertyMappings[4].As(); 56 | 57 | first.AttributeName.Should().Be.EqualTo("prop2"); 58 | first.PropertyInfo.Should().Not.Be.Null(); 59 | 60 | second.ReadOnlyConfiguration.Should().Be.EqualTo(ReadOnly.Always); 61 | second.AttributeName.Should().Be.EqualTo("ou"); 62 | second.PropertyInfo.Should().Not.Be.Null(); 63 | 64 | third.PropertyName.Should().Be.EqualTo("Property6"); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/TestSupport/QueryCommands/MockStandardQueryCommand.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | using System.Linq; 3 | using LinqToLdap.Logging; 4 | using LinqToLdap.Mapping; 5 | using LinqToLdap.QueryCommands; 6 | using LinqToLdap.QueryCommands.Options; 7 | 8 | namespace LinqToLdap.Tests.TestSupport.QueryCommands 9 | { 10 | internal class MockStandardQueryCommand : StandardQueryCommand 11 | { 12 | private bool _enablePagedRequestCall = true; 13 | private bool _enableStandardRequestCall = true; 14 | 15 | public MockStandardQueryCommand(IQueryCommandOptions options, IObjectMapping mapping) : base(options, mapping) 16 | { 17 | } 18 | 19 | public MockStandardQueryCommand DisablePagedRequest() 20 | { 21 | _enablePagedRequestCall = false; 22 | return this; 23 | } 24 | 25 | public MockStandardQueryCommand DisableStandardRequest() 26 | { 27 | _enableStandardRequestCall = false; 28 | return this; 29 | } 30 | 31 | public bool HandlePagedRequestCalled { get; set; } 32 | 33 | public bool HandleStandardRequestCalled { get; set; } 34 | 35 | public override object HandlePagedRequest(DirectoryConnection connection, PageResultRequestControl pageRequest, ILinqToLdapLogger log) 36 | { 37 | HandlePagedRequestCalled = true; 38 | return _enablePagedRequestCall ? base.HandlePagedRequest(connection, pageRequest, log) : null; 39 | } 40 | 41 | public override object HandleStandardRequest(DirectoryConnection connection, ILinqToLdapLogger log, int maxSize, bool pagingEnabled) 42 | { 43 | HandleStandardRequestCalled = true; 44 | return _enableStandardRequestCall ? base.HandleStandardRequest(connection, log, maxSize, pagingEnabled) : null; 45 | } 46 | 47 | public SearchRequest GetRequest() 48 | { 49 | return SearchRequest; 50 | } 51 | 52 | public DirectoryControl[] RequestControlsToSearch { get; set; } 53 | protected override T GetControl(DirectoryControlCollection controls) 54 | { 55 | if (RequestControlsToSearch == null) 56 | { 57 | return base.GetControl(controls); 58 | } 59 | return RequestControlsToSearch.FirstOrDefault(c => c is T) as T; 60 | } 61 | 62 | public DirectoryControl[] ResponseControlsToSearch { get; set; } 63 | protected override T GetControl(DirectoryControl[] controls) 64 | { 65 | if (ResponseControlsToSearch == null) 66 | { 67 | return base.GetControl(controls); 68 | } 69 | return ResponseControlsToSearch.FirstOrDefault(c => c is T) as T; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /LinqToLdap/EventListeners/ListenerPostArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.DirectoryServices.Protocols; 3 | 4 | namespace LinqToLdap.EventListeners 5 | { 6 | /// 7 | /// Arguments passed for s. The arguments used by this class are s. 8 | /// 9 | /// The instance for the event. 10 | /// The response from the Directory. 11 | /// The full request that was sent to the server. 12 | public class ListenerPostArgs 13 | where TRequest : DirectoryRequest 14 | where TResponse : DirectoryResponse 15 | where TObject : class 16 | { 17 | #if (NET35 || NET40) 18 | private readonly WeakReference _connection; 19 | private readonly WeakReference _entry; 20 | #else 21 | private readonly WeakReference _connection; 22 | private readonly WeakReference _entry; 23 | #endif 24 | 25 | internal ListenerPostArgs(TObject entry, TRequest request, TResponse response, LdapConnection connection) 26 | { 27 | #if (NET35 || NET40) 28 | _entry = new WeakReference(entry); 29 | _connection = new WeakReference(connection); 30 | #else 31 | _entry = new WeakReference(entry); 32 | _connection = new WeakReference(connection); 33 | #endif 34 | Request = request; 35 | Response = response; 36 | } 37 | 38 | /// 39 | /// The entry. 40 | /// 41 | public TObject Entry 42 | { 43 | get 44 | { 45 | 46 | #if (NET35 || NET40) 47 | return _entry.Target as TObject; 48 | #else 49 | TObject target; 50 | return _entry.TryGetTarget(out target) ? target : default; 51 | #endif 52 | } 53 | } 54 | 55 | /// 56 | /// The . 57 | /// 58 | public TRequest Request { get; private set; } 59 | 60 | /// 61 | /// The . 62 | /// 63 | public TResponse Response { get; private set; } 64 | 65 | /// 66 | /// The connection that will be used when sending the . 67 | /// 68 | public LdapConnection Connection 69 | { 70 | get 71 | { 72 | #if (NET35 || NET40) 73 | return _connection.Target as LdapConnection; 74 | #else 75 | LdapConnection target; 76 | return _connection.TryGetTarget(out target) ? target : null; 77 | #endif 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/ByteArrayPropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | using System.Linq; 3 | 4 | namespace LinqToLdap.Mapping.PropertyMappings 5 | { 6 | internal class ByteArrayPropertyMapping : PropertyMappingGeneric where T : class 7 | { 8 | public ByteArrayPropertyMapping(PropertyMappingArguments arguments) : base(arguments) 9 | { 10 | } 11 | 12 | public override string FormatValueToFilter(object value) 13 | { 14 | return value != null 15 | ? (value as byte[]).ToStringOctet() 16 | : null; 17 | } 18 | 19 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 20 | { 21 | var modification = new DirectoryAttributeModification 22 | { 23 | Name = AttributeName, 24 | Operation = DirectoryAttributeOperation.Replace 25 | }; 26 | var value = (byte[])GetValueForDirectory(instance); 27 | 28 | if (value != null) 29 | { 30 | modification.Add(value); 31 | } 32 | 33 | return modification; 34 | } 35 | 36 | public override object GetValueForDirectory(object instance) 37 | { 38 | var value = GetValue(instance); 39 | return value; 40 | } 41 | 42 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 43 | { 44 | if (value != null) 45 | { 46 | var bytes = value.GetValues(typeof(byte[]))[0] as byte[]; 47 | if (bytes == null) ThrowMappingException(dn); 48 | 49 | return bytes; 50 | } 51 | 52 | AssertNullable(dn); 53 | 54 | return null; 55 | } 56 | 57 | public override bool IsEqual(object instance, object value, out DirectoryAttributeModification modification) 58 | { 59 | var propertyValue = GetValue(instance) as byte[]; 60 | var bytes = value as byte[]; 61 | if ((propertyValue == null || propertyValue.Length == 0) && (bytes == null || bytes.Length == 0)) 62 | { 63 | modification = null; 64 | return true; 65 | } 66 | 67 | if (propertyValue == null || bytes == null || bytes.Length != propertyValue.Length) 68 | { 69 | modification = GetDirectoryAttributeModification(instance); 70 | return false; 71 | } 72 | 73 | if (!bytes.SequenceEqual(propertyValue)) 74 | { 75 | modification = GetDirectoryAttributeModification(instance); 76 | return false; 77 | } 78 | 79 | modification = null; 80 | return true; 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /LinqToLdap/ILdapConfiguration.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.EventListeners; 2 | using LinqToLdap.Logging; 3 | using LinqToLdap.Mapping; 4 | using System.Collections.Generic; 5 | using System.DirectoryServices.Protocols; 6 | 7 | namespace LinqToLdap 8 | { 9 | /// 10 | /// Interface for accessing configuration information for connecting, querying, and updating a LDAP server. 11 | /// 12 | public interface ILdapConfiguration 13 | { 14 | /// 15 | /// The configured connection factory to be used for all 16 | /// s that don't explicitly get an . 17 | /// 18 | ILdapConnectionFactory ConnectionFactory { get; } 19 | 20 | /// 21 | /// Used for writing and information to a log. 22 | /// 23 | ILinqToLdapLogger Log { get; } 24 | 25 | /// 26 | /// Get the server max page size. 27 | /// 28 | int ServerMaxPageSize { get; } 29 | 30 | /// 31 | /// Indicates if paging is enabled. 32 | /// 33 | bool PagingEnabled { get; } 34 | 35 | /// 36 | /// Creates a from the configuration. 37 | /// 38 | /// 39 | IDirectoryContext CreateContext(); 40 | 41 | /// 42 | /// Class responsible for mapping objects to directory entries. 43 | /// 44 | IDirectoryMapper Mapper { get; } 45 | 46 | /// 47 | /// Changes the current mapper object for altering mappings at runtime. The default mapper always has a key of . If is null then a default instance will be created with the key. 48 | /// 49 | /// Identifier for the mapper 50 | /// The optional mapper to register otherwise a new instance is created. 51 | /// Throw if is null 52 | IDirectoryMapper ChangeMapper(string key, IDirectoryMapper mapper = null); 53 | 54 | /// 55 | /// Get all event listeners of type registered with this configuration. 56 | /// 57 | /// The type of listeners to retrieve. 58 | /// 59 | IEnumerable GetListeners() where TListener : IEventListener; 60 | 61 | /// 62 | /// Get all event listeners registered with this configuration. 63 | /// 64 | /// 65 | IEnumerable GetListeners(); 66 | } 67 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappingGeneric.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Exceptions; 2 | using LinqToLdap.Mapping.PropertyMappings; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace LinqToLdap.Mapping 7 | { 8 | internal abstract class PropertyMappingGeneric : PropertyMapping where T : class 9 | { 10 | protected PropertyMappingGeneric(PropertyMappingArguments arguments) 11 | : base(arguments.PropertyType, arguments.PropertyName, arguments.AttributeName, arguments.IsDistinguishedName, arguments.ReadOnly) 12 | { 13 | Getter = arguments.Getter; 14 | Setter = arguments.Setter; 15 | 16 | DirectoryValueMappings = arguments.DirectoryMappings; 17 | InstanceValueMappings = arguments.InstanceMappings; 18 | } 19 | 20 | protected Dictionary DirectoryValueMappings { get; private set; } 21 | protected Dictionary InstanceValueMappings { get; private set; } 22 | public Action Setter { get; private set; } 23 | public Func Getter { get; private set; } 24 | 25 | public override object GetValue(object instance) 26 | { 27 | if (Getter == null) 28 | throw new NotSupportedException(string.Format("No getter has been defined for {0}.", typeof(T).FullName)); 29 | 30 | return Getter(instance as T); 31 | } 32 | 33 | public override void SetValue(object instance, object value) 34 | { 35 | if (Setter == null) 36 | throw new NotSupportedException(string.Format("No setter has been defined for {0}.", typeof(T).FullName)); 37 | 38 | Setter(instance as T, value); 39 | } 40 | 41 | protected void ThrowMappingException(object value, string dn, Exception ex = null) 42 | { 43 | throw new MappingException( 44 | string.Format("Value '{0}' returned from directory cannot be converted to {1} for '{2}' on '{3}' - '{4}'", 45 | value, PropertyType.FullName, PropertyName, typeof(T).FullName, dn), ex); 46 | } 47 | 48 | protected void ThrowMappingException(string dn, Exception ex = null) 49 | { 50 | throw new MappingException( 51 | string.Format("Value returned from directory cannot be converted to {0} for '{1}' on '{2}' - '{3}'", 52 | PropertyType.FullName, PropertyName, typeof(T).FullName, dn), ex); 53 | } 54 | 55 | protected void AssertNullable(string dn) 56 | { 57 | if (!IsNullable) 58 | { 59 | throw new MappingException( 60 | string.Format( 61 | "Null or empty returned from directory for non nullable type '{0}' for '{1}' on '{2}' - '{3}'", 62 | PropertyType.FullName, PropertyName, typeof(T).FullName, dn)); 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /LinqToLdap/Helpers/ObjectActivator.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Exceptions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | 7 | namespace LinqToLdap.Helpers 8 | { 9 | internal static class ObjectActivator 10 | { 11 | #if (NET35 || NET40) 12 | private readonly static LinqToLdap.Collections.SafeDictionary _constructors = new LinqToLdap.Collections.SafeDictionary(); 13 | #else 14 | private readonly static System.Collections.Concurrent.ConcurrentDictionary _constructors = new System.Collections.Concurrent.ConcurrentDictionary(); 15 | #endif 16 | 17 | public static object CreateGenericInstance(Type instanceType, Type genericParameterType, object[] parameters, string key) 18 | { 19 | return _constructors.GetOrAdd(instanceType.FullName + genericParameterType.FullName + (key ?? string.Empty), t => 20 | { 21 | var type = instanceType.MakeGenericType(genericParameterType); 22 | var constructors = type.GetConstructors().Where(x => x.GetParameters().Length == parameters.Length).ToArray(); 23 | 24 | ConstructorInfo constructor = constructors.Length == 1 ? constructors[0] : null; 25 | 26 | if (constructor == null) 27 | { 28 | var ranked = AnonList(new { Key = default(int), Value = default(ConstructorInfo) }); 29 | foreach (var ctor in constructors) 30 | { 31 | var constructorParameters = ctor.GetParameters(); 32 | //find best parameter fit 33 | int matches = 0; 34 | for (int i = 0; i < parameters.Length; i++) 35 | { 36 | var parameter = parameters[i]; 37 | var constructorParameter = constructorParameters[i]; 38 | if (parameter == null) continue; 39 | 40 | if (constructorParameter.ParameterType.IsAssignableFrom(parameter.GetType())) 41 | { 42 | matches++; 43 | } 44 | } 45 | ranked.Add(new { Key = matches, Value = ctor }); 46 | } 47 | 48 | constructor = ranked.OrderByDescending(x => x.Key).Select(x => x.Value).FirstOrDefault(); 49 | } 50 | if (constructor == null) throw new MissingConstructorException($"Missing constructor for {type.FullName}"); 51 | 52 | return DelegateBuilder.BuildUnknownCtorWithParams(constructor); 53 | }).Invoke(parameters); 54 | } 55 | 56 | private static List AnonList(T _) 57 | { 58 | return new List(); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/QueryCommand.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Logging; 2 | using LinqToLdap.Mapping; 3 | using LinqToLdap.QueryCommands.Options; 4 | using System; 5 | using System.DirectoryServices.Protocols; 6 | using System.Linq; 7 | 8 | namespace LinqToLdap.QueryCommands 9 | { 10 | internal abstract class QueryCommand : IQueryCommand 11 | { 12 | protected readonly SearchRequest SearchRequest; 13 | protected readonly IQueryCommandOptions Options; 14 | protected readonly IObjectMapping Mapping; 15 | 16 | protected QueryCommand(IQueryCommandOptions options, IObjectMapping mapping, bool initializeAttributes) 17 | { 18 | Options = options; 19 | Mapping = mapping; 20 | SearchRequest = new SearchRequest { Filter = options.Filter }; 21 | if (Options.Controls != null) 22 | { 23 | SearchRequest.Controls.AddRange(Options.Controls.ToArray()); 24 | } 25 | if (initializeAttributes) 26 | { 27 | InitializeAttributes(); 28 | } 29 | } 30 | 31 | private void InitializeAttributes() 32 | { 33 | if (!Mapping.HasCatchAllMapping) 34 | { 35 | var attributes = Mapping.HasSubTypeMappings 36 | ? Options.AttributesToLoad.Values 37 | .Union(new[] { "objectClass" }, StringComparer.OrdinalIgnoreCase) 38 | .ToArray() 39 | : Options.AttributesToLoad.Values.ToArray(); 40 | SearchRequest.Attributes.AddRange(attributes); 41 | } 42 | } 43 | 44 | protected virtual T GetControl(DirectoryControl[] controls) where T : class 45 | { 46 | if (controls == null || controls.Length == 0) return default; 47 | 48 | return controls.FirstOrDefault(c => c is T) as T; 49 | } 50 | 51 | protected virtual T GetControl(DirectoryControlCollection controls) where T : class 52 | { 53 | if (controls == null || controls.Count == 0) return default; 54 | 55 | return controls.OfType().FirstOrDefault(); 56 | } 57 | 58 | public abstract object Execute(DirectoryConnection connection, SearchScope scope, int maxPageSize, bool pagingEnabled, ILinqToLdapLogger log = null, string namingContext = null); 59 | 60 | #if !NET35 && !NET40 61 | 62 | public abstract System.Threading.Tasks.Task ExecuteAsync(LdapConnection connection, SearchScope scope, int maxPageSize, bool pagingEnabled, ILinqToLdapLogger log = null, string namingContext = null); 63 | 64 | #endif 65 | 66 | protected void SetDistinguishedName(string namingContext) 67 | { 68 | SearchRequest.DistinguishedName = namingContext ?? Mapping.NamingContext; 69 | } 70 | 71 | public override string ToString() 72 | { 73 | return SearchRequest.ToLogString(); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/StringArrayPropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.DirectoryServices.Protocols; 3 | using System.Linq; 4 | 5 | namespace LinqToLdap.Mapping.PropertyMappings 6 | { 7 | internal class StringArrayPropertyMapping : PropertyMappingGeneric where T : class 8 | { 9 | public StringArrayPropertyMapping(PropertyMappingArguments arguments) 10 | : base(arguments) 11 | { 12 | } 13 | 14 | public override string FormatValueToFilter(object value) 15 | { 16 | if (!(value is string)) 17 | throw new NotSupportedException("String arrays cannot be used in filters."); 18 | 19 | return value.ToString().CleanFilterValue(); 20 | } 21 | 22 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 23 | { 24 | var modification = new DirectoryAttributeModification 25 | { 26 | Name = AttributeName, 27 | Operation = DirectoryAttributeOperation.Replace 28 | }; 29 | var value = (string[])GetValueForDirectory(instance); 30 | 31 | if (value != null) 32 | { 33 | foreach (var s in value) 34 | { 35 | modification.Add(s); 36 | } 37 | } 38 | 39 | return modification; 40 | } 41 | 42 | public override object GetValueForDirectory(object instance) 43 | { 44 | return GetValue(instance); 45 | } 46 | 47 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 48 | { 49 | if (value != null) 50 | { 51 | string[] strings = Array.ConvertAll(value.GetValues(typeof(string)), obj => (string)obj); 52 | 53 | return strings; 54 | } 55 | 56 | AssertNullable(dn); 57 | 58 | return null; 59 | } 60 | 61 | public override bool IsEqual(object instance, object value, out DirectoryAttributeModification modification) 62 | { 63 | var propertyValue = GetValue(instance) as string[]; 64 | var strings = value as string[]; 65 | if ((propertyValue == null || propertyValue.Length == 0) && (strings == null || strings.Length == 0)) 66 | { 67 | modification = null; 68 | return true; 69 | } 70 | 71 | if (propertyValue == null || strings == null || strings.Length != propertyValue.Length) 72 | { 73 | modification = GetDirectoryAttributeModification(instance); 74 | return false; 75 | } 76 | 77 | if (!strings.SequenceEqual(propertyValue)) 78 | { 79 | modification = GetDirectoryAttributeModification(instance); 80 | return false; 81 | } 82 | 83 | modification = null; 84 | return true; 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/ByteArrayArrayPropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.DirectoryServices.Protocols; 3 | using System.Linq; 4 | 5 | namespace LinqToLdap.Mapping.PropertyMappings 6 | { 7 | internal class ByteArrayArrayPropertyMapping : PropertyMappingGeneric where T : class 8 | { 9 | public ByteArrayArrayPropertyMapping(PropertyMappingArguments arguments) 10 | : base(arguments) 11 | { 12 | } 13 | 14 | public override string FormatValueToFilter(object value) 15 | { 16 | if (!(value is byte[])) 17 | throw new NotSupportedException("Byte[][] cannot be used in filters."); 18 | 19 | return (value as byte[]).ToStringOctet(); 20 | } 21 | 22 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 23 | { 24 | var modification = new DirectoryAttributeModification 25 | { 26 | Name = AttributeName, 27 | Operation = DirectoryAttributeOperation.Replace 28 | }; 29 | var value = (byte[][])GetValueForDirectory(instance); 30 | 31 | if (value != null) 32 | { 33 | foreach (var b in value) 34 | { 35 | modification.Add(b); 36 | } 37 | } 38 | 39 | return modification; 40 | } 41 | 42 | public override object GetValueForDirectory(object instance) 43 | { 44 | var value = GetValue(instance); 45 | return value; 46 | } 47 | 48 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 49 | { 50 | if (value != null) 51 | { 52 | byte[][] bytes = Array.ConvertAll(value.GetValues(typeof(byte[])), obj => (byte[])obj); 53 | 54 | return bytes; 55 | } 56 | 57 | AssertNullable(dn); 58 | 59 | return null; 60 | } 61 | 62 | public override bool IsEqual(object instance, object value, out DirectoryAttributeModification modification) 63 | { 64 | var propertyValue = GetValue(instance) as byte[][]; 65 | var bytes = value as byte[][]; 66 | if ((propertyValue == null || propertyValue.Length == 0) && (bytes == null || bytes.Length == 0)) 67 | { 68 | modification = null; 69 | return true; 70 | } 71 | 72 | if (propertyValue == null || bytes == null || bytes.Length != propertyValue.Length) 73 | { 74 | modification = GetDirectoryAttributeModification(instance); 75 | return false; 76 | } 77 | 78 | if (bytes.Where((t, i) => !propertyValue[i].SequenceEqual(t)).Any()) 79 | { 80 | modification = GetDirectoryAttributeModification(instance); 81 | return false; 82 | } 83 | 84 | modification = null; 85 | return true; 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.DirectoryServices.Protocols; 3 | 4 | namespace LinqToLdap.Mapping 5 | { 6 | internal abstract class PropertyMapping : IPropertyMapping 7 | { 8 | protected PropertyMapping(Type propertyType, string propertyName, string attributeName, bool isDistinguishedName, ReadOnly readOnly) 9 | { 10 | IsDistinguishedName = isDistinguishedName; 11 | ReadOnly = isDistinguishedName ? ReadOnly.Always : readOnly; 12 | PropertyType = propertyType; 13 | PropertyName = propertyName; 14 | AttributeName = attributeName; 15 | 16 | IsNullable = DetermineIfNullable(); 17 | UnderlyingType = IsNullable && PropertyType.IsValueType 18 | ? Nullable.GetUnderlyingType(PropertyType) 19 | : PropertyType; 20 | 21 | DefaultValue = GetType() 22 | .GetMethod("GetDefault") 23 | .MakeGenericMethod(PropertyType).Invoke(this, null); 24 | } 25 | 26 | private bool DetermineIfNullable() 27 | { 28 | if (!PropertyType.IsValueType) return true; 29 | return Nullable.GetUnderlyingType(PropertyType) != null; 30 | } 31 | 32 | public bool IsNullable { get; private set; } 33 | protected object DefaultValue { get; private set; } 34 | 35 | public Type PropertyType { get; private set; } 36 | public Type UnderlyingType { get; private set; } 37 | 38 | public ReadOnly ReadOnly { get; private set; } 39 | 40 | public bool IsDistinguishedName { get; private set; } 41 | 42 | public string PropertyName { get; private set; } 43 | public string AttributeName { get; private set; } 44 | 45 | public abstract object GetValue(object instance); 46 | 47 | public abstract void SetValue(object instance, object value); 48 | 49 | public virtual object Default() 50 | { 51 | return DefaultValue; 52 | } 53 | 54 | public abstract object FormatValueFromDirectory(DirectoryAttribute value, string dn); 55 | 56 | public abstract string FormatValueToFilter(object value); 57 | 58 | public abstract DirectoryAttributeModification GetDirectoryAttributeModification(object instance); 59 | 60 | public virtual DirectoryAttribute GetDirectoryAttribute(object instance) 61 | { 62 | return GetDirectoryAttributeModification(instance); 63 | } 64 | 65 | public virtual bool IsEqual(object instance, object value, out DirectoryAttributeModification modification) 66 | { 67 | if (!Equals(GetValue(instance), value)) 68 | { 69 | modification = GetDirectoryAttributeModification(instance); 70 | return false; 71 | } 72 | modification = null; 73 | return true; 74 | } 75 | 76 | public abstract object GetValueForDirectory(object instance); 77 | 78 | public TType GetDefault() 79 | { 80 | return default; 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /LinqToLdap/Logging/TraceTextWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace LinqToLdap.Logging 7 | { 8 | /// 9 | /// Simple implementation of that writes to . 10 | /// Pulled from http://damieng.com/blog/2008/07/30/linq-to-sql-log-to-debug-window-file-memory-or-multiple-writers. 11 | /// 12 | public class TraceTextWriter : TextWriter 13 | { 14 | private TraceTextWriter() 15 | { 16 | } 17 | 18 | /// 19 | /// Lazy instance of this class. 20 | /// 21 | public static TraceTextWriter Instance { get { return Nested.NestedInstance; } } 22 | 23 | private class Nested 24 | { 25 | // Explicit static constructor to tell C# compiler 26 | // not to mark type as beforefieldinit 27 | static Nested() 28 | { 29 | } 30 | 31 | internal static readonly TraceTextWriter NestedInstance = new TraceTextWriter(); 32 | } 33 | 34 | /// 35 | /// Delegates writing to 36 | /// 37 | /// buffer 38 | /// index 39 | /// count 40 | public override void Write(char[] buffer, int index, int count) 41 | { 42 | Trace.Write(new String(buffer, index, count)); 43 | } 44 | 45 | /// 46 | /// Delegates writing to 47 | /// 48 | /// The value. 49 | public override void Write(string value) 50 | { 51 | Trace.Write(value); 52 | } 53 | 54 | /// 55 | /// Delegates writing to 56 | /// 57 | /// buffer 58 | /// index 59 | /// count 60 | public override void WriteLine(char[] buffer, int index, int count) 61 | { 62 | Trace.WriteLine(new String(buffer, index, count)); 63 | } 64 | 65 | /// 66 | /// Delegates writing to 67 | /// 68 | /// The value. 69 | public override void WriteLine(string value) 70 | { 71 | Trace.WriteLine(value); 72 | } 73 | 74 | /// 75 | /// Calls and passes . 76 | /// 77 | public override void WriteLine() 78 | { 79 | Trace.WriteLine(string.Empty); 80 | } 81 | 82 | /// 83 | /// Defaults to . 84 | /// 85 | public override Encoding Encoding 86 | { 87 | get { return Encoding.Default; } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/StringCollectionPropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | using System.Linq; 5 | 6 | namespace LinqToLdap.Mapping.PropertyMappings 7 | { 8 | internal class StringCollectionPropertyMapping : PropertyMappingGeneric where T : class 9 | { 10 | public StringCollectionPropertyMapping(PropertyMappingArguments arguments) 11 | : base(arguments) 12 | { 13 | } 14 | 15 | public override string FormatValueToFilter(object value) 16 | { 17 | if (!(value is string)) 18 | throw new NotSupportedException("String collections cannot be used in filters."); 19 | 20 | return value.ToString().CleanFilterValue(); 21 | } 22 | 23 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 24 | { 25 | var modification = new DirectoryAttributeModification 26 | { 27 | Name = AttributeName, 28 | Operation = DirectoryAttributeOperation.Replace 29 | }; 30 | var value = (IEnumerable)GetValueForDirectory(instance); 31 | 32 | if (value != null) 33 | { 34 | foreach (var str in value) 35 | { 36 | modification.Add(str); 37 | } 38 | } 39 | 40 | return modification; 41 | } 42 | 43 | public override object GetValueForDirectory(object instance) 44 | { 45 | return GetValue(instance); 46 | } 47 | 48 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 49 | { 50 | if (value != null) 51 | { 52 | var list = value.GetValues(typeof(string)) 53 | .Select(o => (string)o); 54 | return 55 | 56 | new System.Collections.ObjectModel.Collection(list.ToList()); 57 | } 58 | 59 | AssertNullable(dn); 60 | 61 | return null; 62 | } 63 | 64 | public override bool IsEqual(object instance, object value, out DirectoryAttributeModification modification) 65 | { 66 | var propertyValue = GetValue(instance) as ICollection; 67 | var strings = value as ICollection; 68 | if ((propertyValue == null || propertyValue.Count == 0) && (strings == null || strings.Count == 0)) 69 | { 70 | modification = null; 71 | return true; 72 | } 73 | 74 | if (propertyValue == null || strings == null || strings.Count != propertyValue.Count) 75 | { 76 | modification = GetDirectoryAttributeModification(instance); 77 | return false; 78 | } 79 | 80 | if (!strings.SequenceEqual(propertyValue)) 81 | { 82 | modification = GetDirectoryAttributeModification(instance); 83 | return false; 84 | } 85 | 86 | modification = null; 87 | return true; 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappingBuilders/IDirectoryValueConversionMapper.cs: -------------------------------------------------------------------------------- 1 | namespace LinqToLdap.Mapping.PropertyMappingBuilders 2 | { 3 | /// 4 | /// Interface for directory value conversion to instance value. 5 | /// 6 | /// Type of the mapping builder 7 | /// The property type 8 | public interface IDirectoryToConversion 9 | { 10 | /// 11 | /// The value to return in place of the encountered directory value. 12 | /// 13 | /// The instance value 14 | /// 15 | TPropertyMappingBuilder Returns(TProperty value); 16 | } 17 | 18 | /// 19 | /// The mapper method for the directory value. 20 | /// 21 | /// The type of the property mapping builder. 22 | /// The type of the property. 23 | public interface IDirectoryValueConversionMapper 24 | { 25 | /// 26 | /// Maps a single-valued directory value. 27 | /// 28 | /// The value from the directory. 29 | /// 30 | IDirectoryToConversion DirectoryValue(string directoryValue); 31 | 32 | /// 33 | /// Maps a not set or empty value from the directory. 34 | /// 35 | /// 36 | IDirectoryToConversion DirectoryValueNotSetOrEmpty(); 37 | } 38 | 39 | /// 40 | /// Interface for instance value conversion to a directory value. 41 | /// 42 | /// Type of the mapping builder 43 | public interface IInstanceToConversion 44 | { 45 | /// 46 | /// The value to send in place of the encountered instance value. 47 | /// 48 | /// The directory value 49 | /// 50 | TPropertyMappingBuilder Sends(string value); 51 | } 52 | 53 | /// 54 | /// The mapper method for the instance value. 55 | /// 56 | /// 57 | /// 58 | public interface IInstanceValueConversionMapper 59 | { 60 | /// 61 | /// Maps a single-valued instace value. 62 | /// 63 | /// The value from the instance. 64 | /// 65 | IInstanceToConversion InstanceValue(TProperty instacneValue); 66 | 67 | /// 68 | /// Maps a not null or default value from the instance. 69 | /// 70 | /// 71 | IInstanceToConversion InstanceValueNullOrDefault(); 72 | } 73 | } -------------------------------------------------------------------------------- /LinqToLdap.Tests/Mapping/LdapConfigurationTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.DirectoryServices.Protocols; 3 | using System.Net; 4 | using System.Reflection; 5 | using LinqToLdap.Mapping; 6 | using LinqToLdap.Tests.ClassMapAssembly; 7 | using LinqToLdap.Tests.TestSupport.ExtensionMethods; 8 | using Moq; 9 | using Microsoft.VisualStudio.TestTools.UnitTesting; 10 | using SharpTestsEx; 11 | 12 | namespace LinqToLdap.Tests.Mapping 13 | { 14 | [TestClass] 15 | public class LdapConfigurationTest 16 | { 17 | [TestMethod] 18 | public void GetConnection_CredentialsAndAuthType_ReturnsConnectionWithCredentialsAndAnonymousAuthType() 19 | { 20 | //prepare 21 | var config = new LdapConfiguration(); 22 | 23 | config.ConfigureFactory("server") 24 | .AuthenticateAs(CredentialCache.DefaultNetworkCredentials) 25 | .AuthenticateBy(AuthType.Anonymous); 26 | 27 | //act 28 | var connection = config.ConnectionFactory.GetConnection(); 29 | 30 | //assert 31 | connection.AuthType.Should().Be.EqualTo(AuthType.Anonymous); 32 | #if (NET35 || NET40 || NET45) 33 | connection.FieldValueEx("directoryCredential") 34 | .Should().Not.Be.Null(); 35 | #endif 36 | } 37 | 38 | [TestMethod] 39 | public void AddMapping_AddsMapping() 40 | { 41 | //prepare 42 | var config = new LdapConfiguration(); 43 | var mapper = new Mock(); 44 | var classMap = new Mock(); 45 | config.ChangeMapper("", mapper.Object); 46 | 47 | //act 48 | config.AddMapping(classMap.Object, "nc", new[] { "oc" }, false, "oc", false); 49 | 50 | //assert 51 | mapper.Verify(m => m.Map(classMap.Object, "nc", new[] { "oc" }, false, "oc", false)); 52 | } 53 | 54 | [TestMethod] 55 | public void AddMappingFromAssembly_AssemblyName_AddsMappings() 56 | { 57 | //prepare 58 | var config = new LdapConfiguration(); 59 | var mapper = new Mock(); 60 | config.ChangeMapper("", mapper.Object); 61 | 62 | //act 63 | config.AddMappingsFrom("assemblyname"); 64 | 65 | //assert 66 | mapper.Verify(m => m.AddMappingsFrom("assemblyname")); 67 | } 68 | 69 | [TestMethod] 70 | public void AddMappingFromAssembly_Assembly_AddsMappings() 71 | { 72 | //prepare 73 | var config = new LdapConfiguration(); 74 | var mapper = new Mock(); 75 | config.ChangeMapper("", mapper.Object); 76 | var assembly = Assembly.GetAssembly(typeof(AssemblyTestClass)); 77 | 78 | //act 79 | config.AddMappingsFrom(assembly); 80 | 81 | //assert 82 | mapper.Verify(m => m.AddMappingsFrom(assembly)); 83 | } 84 | 85 | [TestMethod] 86 | public void AddMappingFromAssembly_EmptyAssemblyName_ThrowsArgumentNullException() 87 | { 88 | //assert 89 | Executing.This(() => new LdapConfiguration().AddMappingsFrom("")) 90 | .Should().Throw(); 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /LinqToLdap/QueryCommands/AnyQueryCommand.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Logging; 2 | using LinqToLdap.Mapping; 3 | using LinqToLdap.QueryCommands.Options; 4 | using System; 5 | using System.DirectoryServices.Protocols; 6 | 7 | namespace LinqToLdap.QueryCommands 8 | { 9 | internal class AnyQueryCommand : QueryCommand 10 | { 11 | public AnyQueryCommand(IQueryCommandOptions options, IObjectMapping mapping) 12 | : base(options, mapping, false) 13 | { 14 | } 15 | 16 | public override object Execute(DirectoryConnection connection, SearchScope scope, int maxPageSize, bool pagingEnabled, ILinqToLdapLogger log = null, string namingContext = null) 17 | { 18 | if (Options.YieldNoResults) return false; 19 | 20 | BuildRequest(scope, maxPageSize, pagingEnabled, log, namingContext); 21 | 22 | var response = connection.SendRequest(SearchRequest) as SearchResponse; 23 | 24 | return HandleResponse(response); 25 | } 26 | 27 | #if !NET35 && !NET40 28 | 29 | public override async System.Threading.Tasks.Task ExecuteAsync(LdapConnection connection, SearchScope scope, int maxPageSize, bool pagingEnabled, ILinqToLdapLogger log = null, string namingContext = null) 30 | { 31 | if (Options.YieldNoResults) return false; 32 | 33 | BuildRequest(scope, maxPageSize, pagingEnabled, log, namingContext); 34 | 35 | #if NET45 36 | return await System.Threading.Tasks.Task.Factory.FromAsync( 37 | (callback, state) => 38 | { 39 | return connection.BeginSendRequest(SearchRequest, Options.AsyncProcessing, callback, state); 40 | }, 41 | (asyncresult) => 42 | { 43 | var response = (SearchResponse)connection.EndSendRequest(asyncresult); 44 | return HandleResponse(response); 45 | }, 46 | null 47 | ).ConfigureAwait(false); 48 | #else 49 | var response = await System.Threading.Tasks.Task.Run(() => connection.SendRequest(SearchRequest) as SearchResponse).ConfigureAwait(false); 50 | return HandleResponse(response); 51 | #endif 52 | } 53 | 54 | #endif 55 | 56 | private void BuildRequest(SearchScope scope, int maxPageSize, bool pagingEnabled, ILinqToLdapLogger log = null, string namingContext = null) 57 | { 58 | SetDistinguishedName(namingContext); 59 | SearchRequest.Scope = scope; 60 | if (GetControl(SearchRequest.Controls) != null) 61 | { 62 | throw new InvalidOperationException("Only one page request control can be sent to the server."); 63 | } 64 | if (pagingEnabled && !Options.WithoutPaging) 65 | { 66 | SearchRequest.Controls.Add(new PageResultRequestControl(1)); 67 | } 68 | 69 | SearchRequest.TypesOnly = true; 70 | SearchRequest.Attributes.Add("cn"); 71 | 72 | if (log != null && log.TraceEnabled) log.Trace(SearchRequest.ToLogString()); 73 | } 74 | 75 | private object HandleResponse(SearchResponse response) 76 | { 77 | response.AssertSuccess(); 78 | 79 | return response.Entries.Count > 0; 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /LinqToLdap/DirectoryQuery.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Helpers; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Linq.Expressions; 7 | 8 | namespace LinqToLdap 9 | { 10 | internal class DirectoryQuery : IOrderedQueryable 11 | { 12 | private readonly QueryProvider _provider; 13 | private readonly Expression _expression; 14 | 15 | public DirectoryQuery(QueryProvider provider) 16 | { 17 | _provider = provider ?? throw new ArgumentNullException("provider"); 18 | _expression = Expression.Constant(this); 19 | } 20 | 21 | public DirectoryQuery(QueryProvider provider, Expression expression) 22 | { 23 | _provider = provider ?? throw new ArgumentNullException("provider"); 24 | _expression = expression ?? throw new ArgumentNullException("expression"); 25 | } 26 | 27 | /// 28 | /// Gets the expression tree that is associated with the instance of . 29 | /// 30 | /// 31 | /// The that is associated with this instance of . 32 | /// 33 | public Expression Expression 34 | { 35 | get { return _expression; } 36 | } 37 | 38 | /// 39 | /// Gets the type of the element(s) that are returned when the expression tree associated with this instance of is executed. 40 | /// 41 | /// 42 | /// A that represents the type of the element(s) that are returned when the expression tree associated with this object is executed. 43 | /// 44 | public Type ElementType 45 | { 46 | get { return typeof(T); } 47 | } 48 | 49 | /// 50 | /// Gets the query provider that is associated with this data source. 51 | /// 52 | /// 53 | /// The that is associated with this data source. 54 | /// 55 | public IQueryProvider Provider 56 | { 57 | get { return _provider; } 58 | } 59 | 60 | /// 61 | /// Returns an enumerator that iterates through the collection. 62 | /// 63 | /// 64 | /// A that can be used to iterate through the collection. 65 | /// 66 | /// 1 67 | public IEnumerator GetEnumerator() 68 | { 69 | var enumerator = _provider.Execute(_expression); 70 | return ((IEnumerable)enumerator).GetEnumerator(); 71 | } 72 | 73 | /// 74 | /// Returns an enumerator that iterates through a collection. 75 | /// 76 | /// 77 | /// An object that can be used to iterate through the collection. 78 | /// 79 | /// 2 80 | IEnumerator IEnumerable.GetEnumerator() 81 | { 82 | return GetEnumerator(); 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /LinqToLdap/ILdapConnectionFactoryConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | using System.Net; 3 | 4 | namespace LinqToLdap 5 | { 6 | /// 7 | /// Interface for configuring a . 8 | /// 9 | public interface ILdapConnectionFactoryConfiguration 10 | { 11 | /// 12 | /// Allows you to specify an authentication method for the 13 | /// connection. If this method is not called, the authentication method 14 | /// will be resolved by the . 15 | /// 16 | /// 17 | /// The type of authentication to use. 18 | /// 19 | /// 20 | ILdapConnectionFactoryConfiguration AuthenticateBy(AuthType authType); 21 | 22 | /// 23 | /// Allows you to specify credentials for the connection to use. 24 | /// If this method is not called, then the 25 | /// will use the credentials of the current user. 26 | /// 27 | /// 28 | /// The credentials to use. 29 | /// 30 | /// 31 | ILdapConnectionFactoryConfiguration AuthenticateAs(NetworkCredential credentials); 32 | 33 | /// 34 | /// Specifies the LDAP protocol version. The default is 3. 35 | /// 36 | /// The protocol version 37 | /// 38 | ILdapConnectionFactoryConfiguration ProtocolVersion(int version); 39 | 40 | /// 41 | /// Sets the port manually for the LDAP server. The default is 389. 42 | /// 43 | /// 44 | /// The port to use when communicating with the LDAP server. 45 | /// 46 | /// 47 | ILdapConnectionFactoryConfiguration UsePort(int port); 48 | 49 | /// 50 | /// Sets the connection timeout period in seconds. The default is 30 seconds. 51 | /// 52 | /// The timeout period 53 | /// Thrown if is less than or equal to 0 54 | /// 55 | ILdapConnectionFactoryConfiguration ConnectionTimeoutIn(double seconds); 56 | 57 | /// 58 | /// Turns on SSL and optionally you can set the SSL port. Default is 636. 59 | /// 60 | /// The optional port to use. 61 | /// 62 | ILdapConnectionFactoryConfiguration UseSsl(int port = ConnectionFactoryBase.SslPort); 63 | 64 | /// 65 | /// If this option is called, the server name is a fully-qualified DNS host name. 66 | /// Otherwise the server name can be an IP address, a DNS domain or host name. 67 | /// 68 | /// 69 | ILdapConnectionFactoryConfiguration ServerNameIsFullyQualified(); 70 | 71 | /// 72 | /// Indicates that the connections will use UDP (User Datagram Protocol). 73 | /// 74 | /// 75 | ILdapConnectionFactoryConfiguration UseUdp(); 76 | } 77 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/ByteArrayCollectionPropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | using System.Linq; 5 | 6 | namespace LinqToLdap.Mapping.PropertyMappings 7 | { 8 | internal class ByteArrayCollectionPropertyMapping : PropertyMappingGeneric where T : class 9 | { 10 | public ByteArrayCollectionPropertyMapping(PropertyMappingArguments arguments) 11 | : base(arguments) 12 | { 13 | } 14 | 15 | public override string FormatValueToFilter(object value) 16 | { 17 | if (!(value is byte[])) 18 | throw new NotSupportedException("Byte[][] cannot be used in filters."); 19 | 20 | return (value as byte[]).ToStringOctet(); 21 | } 22 | 23 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 24 | { 25 | var modification = new DirectoryAttributeModification 26 | { 27 | Name = AttributeName, 28 | Operation = DirectoryAttributeOperation.Replace 29 | }; 30 | var value = (IEnumerable)GetValueForDirectory(instance); 31 | 32 | if (value != null) 33 | { 34 | foreach (var b in value) 35 | { 36 | modification.Add(b); 37 | } 38 | } 39 | 40 | return modification; 41 | } 42 | 43 | public override object GetValueForDirectory(object instance) 44 | { 45 | var value = GetValue(instance); 46 | return value; 47 | } 48 | 49 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 50 | { 51 | if (value != null) 52 | { 53 | var bytes = value.GetValues(typeof(byte[])) 54 | .Select(c => (byte[])c); 55 | 56 | return new System.Collections.ObjectModel.Collection(bytes.ToList()); 57 | } 58 | 59 | AssertNullable(dn); 60 | 61 | return null; 62 | } 63 | 64 | public override bool IsEqual(object instance, object value, out DirectoryAttributeModification modification) 65 | { 66 | var propertyValue = GetValue(instance) as ICollection; 67 | var bytes = value as ICollection; 68 | if ((propertyValue == null || propertyValue.Count == 0) && (bytes == null || bytes.Count == 0)) 69 | { 70 | modification = null; 71 | return true; 72 | } 73 | 74 | if (propertyValue == null || bytes == null || bytes.Count != propertyValue.Count) 75 | { 76 | modification = GetDirectoryAttributeModification(instance); 77 | return false; 78 | } 79 | 80 | var propertyEnum = propertyValue.GetEnumerator(); 81 | var bytesEnum = bytes.GetEnumerator(); 82 | while (propertyEnum.MoveNext() && bytesEnum.MoveNext()) 83 | { 84 | if (propertyEnum.Current.SequenceEqual(bytesEnum.Current)) continue; 85 | 86 | modification = GetDirectoryAttributeModification(instance); 87 | return false; 88 | } 89 | 90 | modification = null; 91 | return true; 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /LinqToLdap/Visitors/BooleanReducer.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | 3 | namespace LinqToLdap.Visitors 4 | { 5 | internal class BooleanReducer : ExpressionVisitor 6 | { 7 | private bool _requiresEvaluation = true; 8 | 9 | protected override Expression VisitBinary(BinaryExpression b) 10 | { 11 | if (b.NodeType == ExpressionType.OrElse) 12 | { 13 | if (b.Left.NodeType == ExpressionType.Constant && b.Left.Type == typeof(bool)) 14 | { 15 | _requiresEvaluation = true; 16 | return Visit(true.Equals((b.Left as ConstantExpression).Value) ? b.Left : b.Right); 17 | } 18 | if (b.Right.NodeType == ExpressionType.Constant && b.Right.Type == typeof(bool)) 19 | { 20 | _requiresEvaluation = true; 21 | return Visit(true.Equals((b.Right as ConstantExpression).Value) ? b.Right : b.Left); 22 | } 23 | } 24 | if (b.NodeType == ExpressionType.AndAlso) 25 | { 26 | if (b.Left.NodeType == ExpressionType.Constant && 27 | b.Right.NodeType == ExpressionType.Constant && 28 | b.Left.Type == typeof(bool) && 29 | b.Right.Type == typeof(bool)) 30 | { 31 | _requiresEvaluation = true; 32 | var leftValue = (b.Left as ConstantExpression).Value; 33 | var rightValue = (b.Right as ConstantExpression).Value; 34 | if (leftValue == rightValue) 35 | { 36 | return Visit(b.Left); 37 | } 38 | if (false.Equals(leftValue)) 39 | { 40 | return Visit(b.Left); 41 | } 42 | if (false.Equals(rightValue)) 43 | { 44 | return Visit(b.Left); 45 | } 46 | } 47 | if (b.Left.NodeType == ExpressionType.Constant && b.Left.Type == typeof(bool)) 48 | { 49 | var value = (b.Left as ConstantExpression).Value; 50 | if (false.Equals(value)) 51 | { 52 | return Visit(b.Left); 53 | } 54 | if (true.Equals(value)) 55 | { 56 | return Visit(b.Right); 57 | } 58 | } 59 | else if (b.Right.NodeType == ExpressionType.Constant && b.Right.Type == typeof(bool)) 60 | { 61 | var value = (b.Right as ConstantExpression).Value; 62 | if (false.Equals(value)) 63 | { 64 | return Visit(b.Right); 65 | } 66 | if (true.Equals(value)) 67 | { 68 | return Visit(b.Left); 69 | } 70 | } 71 | } 72 | return base.VisitBinary(b); 73 | } 74 | 75 | public Expression Reduce(Expression expression) 76 | { 77 | Expression reduced = expression; 78 | while (_requiresEvaluation) 79 | { 80 | _requiresEvaluation = false; 81 | reduced = Visit(reduced); 82 | } 83 | 84 | return reduced; 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/ICustomPropertyMapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.DirectoryServices.Protocols; 3 | 4 | namespace LinqToLdap.Mapping 5 | { 6 | /// 7 | /// Interface for custom mapping properties 8 | /// 9 | public interface ICustomPropertyMapper 10 | { 11 | /// 12 | /// Specify an attribute name for a mapped property. 13 | /// 14 | /// Attribute name in the directory 15 | /// 16 | ICustomPropertyMapper Named(string attributeName); 17 | 18 | /// 19 | /// Specify the function for converting the property value to a valid LDAP filter value. 20 | /// 21 | /// The function 22 | /// 23 | ICustomPropertyMapper ConvertToFilterUsing(Func converter); 24 | 25 | /// 26 | /// Specify the function for converting the to a valid value for the mapped property on a mapped object. 27 | /// 28 | /// THe function 29 | /// 30 | ICustomPropertyMapper ConvertFromDirectoryUsing(Func converter); 31 | 32 | /// 33 | /// Specify the function for converting a property value to a valid directory value. 34 | /// 35 | /// The function 36 | /// 37 | ICustomPropertyMapper ConvertToDirectoryUsing(Func converter); 38 | 39 | /// 40 | /// Specify the function for converting a property value to a valid directory value. 41 | /// 42 | /// The function 43 | /// 44 | ICustomPropertyMapper ConvertToDirectoryUsing(Func converter); 45 | 46 | /// 47 | /// Specify the function for converting a property value to a valid directory value. 48 | /// 49 | /// The function 50 | /// 51 | ICustomPropertyMapper ConvertToDirectoryUsing(Func converter); 52 | 53 | /// 54 | /// Specify the function for converting a property value to a valid directory value. 55 | /// 56 | /// The function 57 | /// 58 | ICustomPropertyMapper ConvertToDirectoryUsing(Func converter); 59 | 60 | /// 61 | /// The equal comparison to use when comparing the original value to the current value. For use with change tracking updatable objects. Will default to standard equals comparison if null. 62 | /// 63 | /// The function 64 | /// 65 | ICustomPropertyMapper CompareChangesUsing(Func comparer); 66 | 67 | /// 68 | /// The read only configuration . 69 | /// 70 | /// 71 | ICustomPropertyMapper ReadOnly(ReadOnly readOnly = Mapping.ReadOnly.Always); 72 | } 73 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/AutoClassMap.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Collections; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace LinqToLdap.Mapping 7 | { 8 | /// 9 | /// Generates a class map via convention. Property names will map directly to their attribute names. 10 | /// 11 | /// Class to map 12 | public class AutoClassMap : ClassMap where T : class 13 | { 14 | /// 15 | /// Maps the schema and property information. 16 | /// 17 | /// The object category for the object. 18 | /// 19 | /// Indicates if the object category should be included in all queries. 20 | /// 21 | /// The location of the objects in the directory. 22 | /// The object classes for the object. 23 | /// Indicates if the object classes should be included in all queries. 24 | /// 25 | public override IClassMap PerformMapping(string namingContext = null, string objectCategory = null, bool includeObjectCategory = true, IEnumerable objectClasses = null, bool includeObjectClasses = true) 26 | { 27 | NamingContext(namingContext); 28 | ObjectCategory(objectCategory, includeObjectCategory); 29 | ObjectClasses(objectClasses, includeObjectClasses); 30 | 31 | var type = typeof(T); 32 | 33 | var properties = (IsForAnonymousType 34 | ? type.GetProperties(Flags) 35 | : type.GetProperties(Flags) 36 | .Where(p => p.GetGetMethod() != null && p.GetSetMethod() != null)).ToList(); 37 | 38 | properties 39 | .ForEach( 40 | p => 41 | MapPropertyInfo(p, 42 | p.Name.Equals("DistinguishedName", StringComparison.OrdinalIgnoreCase) || 43 | p.Name.Equals("entrydn", StringComparison.OrdinalIgnoreCase), 44 | (p.Name.Equals("cn", StringComparison.OrdinalIgnoreCase) || 45 | p.Name.Equals("ou", StringComparison.OrdinalIgnoreCase)) ? ReadOnly.Always : default(ReadOnly?))); 46 | 47 | var catchAll = properties 48 | .FirstOrDefault(p => typeof(IDirectoryAttributes).IsAssignableFrom(p.PropertyType)); 49 | 50 | if (catchAll != null) 51 | { 52 | CatchAll(catchAll); 53 | } 54 | 55 | return this; 56 | } 57 | 58 | /// 59 | /// Converts the mapping to functional object mapping. 60 | /// 61 | /// 62 | public override IObjectMapping ToObjectMapping() 63 | { 64 | return IsForAnonymousType 65 | ? new AnonymousObjectMapping(GetNamingContext(), 66 | PropertyMappings.Select(pm => pm.ToPropertyMapping()), 67 | GetObjectCategory(), 68 | IncludeObjectCategory, 69 | GetObjectClass(), 70 | IncludeObjectClasses) 71 | : base.ToObjectMapping(); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /LinqToLdap/Mapping/PropertyMappings/DatePropertyMapping.cs: -------------------------------------------------------------------------------- 1 | using LinqToLdap.Collections; 2 | using System; 3 | using System.DirectoryServices.Protocols; 4 | 5 | namespace LinqToLdap.Mapping.PropertyMappings 6 | { 7 | internal class DatePropertyMapping : PropertyMappingGeneric where T : class 8 | { 9 | private readonly bool _isFileTimeFormat; 10 | private readonly string _dateFormat; 11 | 12 | public DatePropertyMapping(PropertyMappingArguments arguments, string dateFormat) 13 | : base(arguments) 14 | { 15 | _isFileTimeFormat = dateFormat == null; 16 | 17 | _dateFormat = dateFormat; 18 | } 19 | 20 | public override object FormatValueFromDirectory(DirectoryAttribute value, string dn) 21 | { 22 | if (value != null && value.Count > 0 && value[0] is string str && !str.IsNullOrEmpty()) 23 | { 24 | try 25 | { 26 | if (DirectoryValueMappings != null && DirectoryValueMappings.ContainsKey(str)) 27 | { 28 | return DirectoryValueMappings[str]; 29 | } 30 | var dateTime = _isFileTimeFormat 31 | ? DateTime.FromFileTime(long.Parse(str)) 32 | : str.FormatLdapDateTime(_dateFormat); 33 | 34 | return dateTime; 35 | } 36 | catch (Exception ex) 37 | { 38 | ThrowMappingException(value, dn, ex); 39 | } 40 | } 41 | 42 | if (DirectoryValueMappings != null && DirectoryValueMappings.ContainsKey(string.Empty)) 43 | { 44 | return DirectoryValueMappings[string.Empty]; 45 | } 46 | 47 | AssertNullable(dn); 48 | 49 | return null; 50 | } 51 | 52 | public override string FormatValueToFilter(object value) 53 | { 54 | var date = (DateTime)value; 55 | 56 | return _isFileTimeFormat 57 | ? date.ToFileTime().ToString() 58 | : date.FormatLdapDateTime(_dateFormat); 59 | } 60 | 61 | public override DirectoryAttributeModification GetDirectoryAttributeModification(object instance) 62 | { 63 | var modification = new DirectoryAttributeModification 64 | { 65 | Name = AttributeName, 66 | Operation = DirectoryAttributeOperation.Replace 67 | }; 68 | var value = (string)GetValueForDirectory(instance); 69 | 70 | if (!string.IsNullOrEmpty(value)) 71 | { 72 | modification.Add(value); 73 | } 74 | 75 | return modification; 76 | } 77 | 78 | public override object GetValueForDirectory(object instance) 79 | { 80 | var value = GetValue(instance); 81 | 82 | if (value == null) 83 | { 84 | return InstanceValueMappings != null && InstanceValueMappings.ContainsKey(Nothing.Value) 85 | ? InstanceValueMappings[Nothing.Value] 86 | : value; 87 | } 88 | 89 | if (InstanceValueMappings != null && InstanceValueMappings.ContainsKey(value)) 90 | { 91 | return InstanceValueMappings[value]; 92 | } 93 | 94 | return _isFileTimeFormat 95 | ? ((DateTime)value).ToFileTime().ToString() 96 | : ((DateTime)value).FormatLdapDateTime(_dateFormat); 97 | } 98 | } 99 | } --------------------------------------------------------------------------------