Skip to content

Commit f752a2e

Browse files
committed
Added Assembly attribute for ADO Provider assemblies
1 parent c4020eb commit f752a2e

22 files changed

Lines changed: 360 additions & 29 deletions

CommonAssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@
1919
// COM, set the ComVisible attribute to true on that type.
2020
[assembly: ComVisible(false)]
2121

22-
[assembly: AssemblyVersion("0.17.0.0")]
23-
[assembly: AssemblyFileVersion("0.17.0.0")]
22+
[assembly: AssemblyVersion("0.17.0.1")]
23+
[assembly: AssemblyFileVersion("0.17.0.1")]
2424

Simple.Data.Ado.Test/Properties/AssemblyInfo.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Reflection;
22
using System.Runtime.CompilerServices;
33
using System.Runtime.InteropServices;
4+
using Simple.Data.Ado.Test;
45

56
// General Information about an assembly is controlled through the following
67
// set of attributes. Change these attribute values to modify the information
@@ -34,3 +35,5 @@
3435
// [assembly: AssemblyVersion("1.0.*")]
3536
[assembly: AssemblyVersion("1.0.0.0")]
3637
[assembly: AssemblyFileVersion("1.0.0.0")]
38+
39+
[assembly: TestProviderAssembly]

Simple.Data.Ado.Test/ProviderHelperTest.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ public void ShouldReturnNonExportedTypeFromServiceProvider()
3939
Assert.IsInstanceOf(typeof(IQueryPager), actual);
4040
}
4141

42+
[Test]
43+
public void ShouldFindProviderUsingAssemblyAttribute()
44+
{
45+
IConnectionProvider provider;
46+
Assert.True(ProviderHelper.TryLoadAssemblyUsingAttribute("Test", null, out provider));
47+
Assert.IsNotNull(provider);
48+
Assert.IsInstanceOf<StubConnectionProvider>(provider);
49+
}
50+
4251
public class StubConnectionAndServiceProvider : IConnectionProvider, IServiceProvider
4352
{
4453
public void SetConnectionString(string connectionString)

Simple.Data.Ado.Test/Simple.Data.Ado.Test.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33
<PropertyGroup>
44
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -63,6 +63,7 @@
6363
<Compile Include="TableCollectionTest.cs" />
6464
<Compile Include="TestCustomInserter.cs" />
6565
<Compile Include="ProviderTest.cs" />
66+
<Compile Include="TestProviderAssemblyAttribute.cs" />
6667
</ItemGroup>
6768
<ItemGroup>
6869
<ProjectReference Include="..\Simple.Data.Ado\Simple.Data.Ado.csproj">
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
3+
namespace Simple.Data.Ado.Test
4+
{
5+
public class TestProviderAssemblyAttribute : ProviderAssemblyAttributeBase
6+
{
7+
public TestProviderAssemblyAttribute()
8+
: base("Test")
9+
{
10+
}
11+
12+
public override bool TryGetProvider(string connectionString, out IConnectionProvider provider, out Exception exception)
13+
{
14+
if (connectionString.Equals("Test"))
15+
{
16+
provider = new StubConnectionProvider();
17+
exception = null;
18+
return true;
19+
}
20+
provider = null;
21+
exception = null;
22+
return false;
23+
}
24+
}
25+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Reflection;
6+
using System.Text;
7+
8+
namespace Simple.Data.Ado
9+
{
10+
[AttributeUsage(AttributeTargets.Assembly)]
11+
public abstract class ProviderAssemblyAttributeBase : Attribute
12+
{
13+
static ProviderAssemblyAttributeBase()
14+
{
15+
//AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomainOnReflectionOnlyAssemblyResolve;
16+
}
17+
18+
private static Assembly CurrentDomainOnReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
19+
{
20+
return Assembly.Load(args.Name);
21+
}
22+
23+
private readonly HashSet<string> _adoProviderNames;
24+
25+
protected ProviderAssemblyAttributeBase(string providerName, params string[] additionalProviderNames)
26+
{
27+
_adoProviderNames = new HashSet<string>(additionalProviderNames, StringComparer.InvariantCultureIgnoreCase) {providerName};
28+
}
29+
30+
public bool IsForProviderName(string adoProviderName)
31+
{
32+
return _adoProviderNames.Contains(adoProviderName);
33+
}
34+
35+
public abstract bool TryGetProvider(string connectionString, out IConnectionProvider provider, out Exception exception);
36+
37+
public static IEnumerable<ProviderAssemblyAttributeBase> Get(Assembly assembly)
38+
{
39+
if (assembly.ReflectionOnly)
40+
{
41+
foreach (var referencedAssembly in assembly.GetReferencedAssemblies())
42+
{
43+
if (AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().All(a => a.GetName().FullName != referencedAssembly.FullName))
44+
{
45+
try
46+
{
47+
Assembly.ReflectionOnlyLoad(referencedAssembly.FullName);
48+
}
49+
catch (FileNotFoundException)
50+
{
51+
return Enumerable.Empty<ProviderAssemblyAttributeBase>();
52+
}
53+
}
54+
}
55+
var hasAttribute = assembly.GetCustomAttributesData().Any(
56+
cad => typeof (ProviderAssemblyAttributeBase).IsAssignableFrom(cad.Constructor.DeclaringType));
57+
if (hasAttribute)
58+
{
59+
assembly = Assembly.Load(assembly.GetName());
60+
}
61+
else
62+
{
63+
return Enumerable.Empty<ProviderAssemblyAttributeBase>();
64+
}
65+
}
66+
return assembly.GetCustomAttributes(typeof (ProviderAssemblyAttributeBase), false)
67+
.Cast<ProviderAssemblyAttributeBase>();
68+
}
69+
}
70+
}

Simple.Data.Ado/ProviderHelper.cs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using System;
22
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
34
using System.ComponentModel.Composition;
45
using System.ComponentModel.Composition.Hosting;
56
using System.Configuration;
67
using System.Data.OleDb;
8+
using System.Linq;
79
using System.Reflection;
810
using System.IO;
911
using System.Text.RegularExpressions;
@@ -99,7 +101,14 @@ public IConnectionProvider GetProviderByConnectionString(string connectionString
99101

100102
private static IConnectionProvider LoadProviderByConnectionToken(ConnectionToken token)
101103
{
102-
var provider = ComposeProvider(token.ProviderName);
104+
IConnectionProvider provider;
105+
106+
if (TryLoadAssemblyUsingAttribute(token.ConnectionString, token.ProviderName, out provider))
107+
{
108+
return provider;
109+
}
110+
111+
provider = ComposeProvider(token.ProviderName);
103112
if (provider == null)
104113
{
105114
throw new InvalidOperationException("Provider could not be resolved.");
@@ -152,6 +161,75 @@ private static T GetCustomProviderExport<T>(ISchemaProvider schemaProvider)
152161
}
153162
}
154163

164+
internal static bool TryLoadAssemblyUsingAttribute(string connectionString, string providerName, out IConnectionProvider connectionProvider)
165+
{
166+
var attributes = LoadAssemblyAttributes();
167+
if (attributes.Count == 0)
168+
{
169+
connectionProvider = null;
170+
return false;
171+
}
172+
if (!string.IsNullOrWhiteSpace(providerName))
173+
{
174+
attributes = attributes.Where(a => a.IsForProviderName(providerName)).ToList();
175+
}
176+
if (attributes.Count == 0)
177+
{
178+
connectionProvider = null;
179+
return false;
180+
}
181+
182+
return LoadUsingAssemblyAttribute(connectionString, attributes, out connectionProvider);
183+
}
184+
185+
private static bool LoadUsingAssemblyAttribute(string connectionString, ICollection<ProviderAssemblyAttributeBase> attributes,
186+
out IConnectionProvider connectionProvider)
187+
{
188+
if (attributes.Count == 0)
189+
{
190+
{
191+
connectionProvider = null;
192+
return true;
193+
}
194+
}
195+
196+
foreach (var attribute in attributes)
197+
{
198+
Exception exception;
199+
if (attribute.TryGetProvider(connectionString, out connectionProvider, out exception))
200+
{
201+
return true;
202+
}
203+
}
204+
connectionProvider = null;
205+
return false;
206+
}
207+
208+
private static List<ProviderAssemblyAttributeBase> LoadAssemblyAttributes()
209+
{
210+
var attributes = AppDomain.CurrentDomain.GetAssemblies()
211+
.Where(a => !a.GlobalAssemblyCache)
212+
.SelectMany(ProviderAssemblyAttributeBase.Get)
213+
.ToList();
214+
215+
if (attributes.Count == 0)
216+
{
217+
foreach (var file in Directory.EnumerateFiles(Composer.GetSimpleDataAssemblyPath(), "*.dll"))
218+
{
219+
Assembly assembly;
220+
if (Composer.TryLoadAssembly(file, out assembly))
221+
{
222+
if (ProviderAssemblyAttributeBase.Get(assembly).Any())
223+
{
224+
assembly = Assembly.LoadFrom(file);
225+
attributes.AddRange(ProviderAssemblyAttributeBase.Get(assembly));
226+
}
227+
}
228+
}
229+
}
230+
return attributes;
231+
}
232+
155233
private class ConnectionToken : IEquatable<ConnectionToken>
156234
{
157235
/// <summary>

Simple.Data.Ado/Simple.Data.Ado.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
<DesignTimeSharedInput>True</DesignTimeSharedInput>
121121
<DependentUpon>Settings.settings</DependentUpon>
122122
</Compile>
123+
<Compile Include="ProviderAssemblyAttributeBase.cs" />
123124
<Compile Include="ProviderHelper.cs" />
124125
<Compile Include="QueryBuilder.cs" />
125126
<Compile Include="QueryBuilderBase.cs" />

Simple.Data.Ado/Simple.Data.Ado.nuspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
33
<metadata>
44
<id>Simple.Data.Ado</id>
5-
<version>1.0.0-rc0</version>
5+
<version>0.17.0.1</version>
66
<authors>Mark Rendle</authors>
77
<owners>Mark Rendle</owners>
88
<description>ADO Adapter for the Simple.Data data access library.</description>
@@ -12,7 +12,7 @@
1212
<tags>sqlserver database data ado .net40</tags>
1313
<language>en-us</language>
1414
<dependencies>
15-
<dependency id="Simple.Data.Core" version="1.0.0-rc0" />
15+
<dependency id="Simple.Data.Core" version="0.17.0.1" />
1616
</dependencies>
1717
</metadata>
1818
</package>

Simple.Data.InMemoryTest/InMemoryTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,5 +661,27 @@ public void UpsertWithoutDefinedKeyColumnsSHouldThrowMeaningfulException()
661661
var exception = Assert.Throws<InvalidOperationException>(() => db.Test.Upsert(Id: 1, HasTowel: true));
662662
Assert.AreEqual("No key columns defined for table \"Test\"", exception.Message);
663663
}
664+
665+
[Test]
666+
public void JoinTest()
667+
{
668+
Guid masterId = Guid.NewGuid();
669+
InMemoryAdapter adapter = new InMemoryAdapter();
670+
adapter.Join.Master("Master", "Id").Detail("Detail", "MasterId");
671+
Database.UseMockAdapter(adapter);
672+
var db = Database.Open();
673+
db.Master.Insert(Id: masterId);
674+
db.Detail.Insert(Id: Guid.NewGuid(), MasterId: masterId, Box: 999);
675+
// Act
676+
IEnumerable<dynamic> list = db.Detail.All()
677+
.Join(db.Master).On(db.Master.Id == db.Detail.MasterId)
678+
.Select(db.Master.Id, db.Detail.Box)
679+
.Cast<dynamic>();
680+
// Assert
681+
dynamic detail = list.FirstOrDefault();
682+
Assert.NotNull(detail);
683+
Assert.That(detail.Id, Is.EqualTo(masterId));
684+
Assert.That(detail.Box, Is.EqualTo(999));
685+
}
664686
}
665687
}

0 commit comments

Comments
 (0)