Skip to content

Commit 69c54cb

Browse files
authored
Merge pull request #38 from SubSonic-Core/dev/33-model-collections
added unit tests and appropriate infrastructure to pass testing
2 parents 9feb5ca + 96296de commit 69c54cb

File tree

22 files changed

+252
-94
lines changed

22 files changed

+252
-94
lines changed

SubSonic.Extensions.Test/Models/Person.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using SubSonic.Collections;
2+
using System;
23
using System.Collections.Generic;
34
using System.ComponentModel.DataAnnotations;
45
using System.ComponentModel.DataAnnotations.Schema;
@@ -27,7 +28,7 @@ public class Person
2728
[MaxLength(104)]
2829
public string FullName { get; set; }
2930

30-
public virtual ICollection<Renter> Renters { get; set; }
31+
public virtual ISubSonicCollection<Renter> Renters { get; set; }
3132

3233
public override string ToString() => FullName;
3334
}

SubSonic.Extensions.Test/Models/RealEstateProperty.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using SubSonic.Infrastructure;
1+
using SubSonic.Collections;
2+
using SubSonic.Infrastructure;
23
using System.Collections.Generic;
34
using System.ComponentModel.DataAnnotations;
45
using System.ComponentModel.DataAnnotations.Schema;
@@ -12,11 +13,6 @@ namespace SubSonic.Extensions.Test.Models
1213
[DbCommandQuery(DbQueryType.Insert, typeof(InsertRealEstateProperty))]
1314
public class RealEstateProperty
1415
{
15-
public RealEstateProperty()
16-
{
17-
Units = new HashSet<Unit>();
18-
}
19-
2016
[Key]
2117
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
2218
public int ID { get; set; }
@@ -30,6 +26,6 @@ public RealEstateProperty()
3026

3127

3228
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "<Pending>")]
33-
public virtual ICollection<Unit> Units { get; set; }
29+
public virtual ISubSonicCollection<Unit> Units { get; set; }
3430
}
3531
}

SubSonic.Extensions.Test/Models/Unit.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ public class Unit
2727
[ForeignKey(nameof(RealEstatePropertyID))]
2828
public virtual RealEstateProperty RealEstateProperty { get;set;}
2929

30-
public virtual ICollection<Renter> Renters { get; set; }
30+
public virtual ISubSonicCollection<Renter> Renters { get; set; }
3131
}
3232
}

SubSonic.Extensions.Test/SubSonic.Extensions.Test.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<RepositoryUrl>https://github.com/kccarter76/SubSonic-Core/tree/master/SubSonic.Extensions.Test</RepositoryUrl>
1515
<PackageLicenseFile>LICENSE.MD</PackageLicenseFile>
1616
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
17-
<Version>4.1.0-alpha.2</Version>
17+
<Version>4.1.0-alpha.3</Version>
1818
<ApplicationIcon />
1919
<OutputType>Library</OutputType>
2020
<NeutralLanguage>en</NeutralLanguage>

SubSonic.Tests/DAL/DbContext/DbInsertTests.cs

Lines changed: 1 addition & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -19,33 +19,8 @@ public partial class DbContextTests
1919
[Order(0)]
2020
public void ShouldBeAbleToInsertOnePersonRecordWithNoUDTT()
2121
{
22-
string expected_cmd = @"INSERT INTO [dbo].[Person]
23-
OUTPUT INSERTED.* INTO @output
24-
VALUES
25-
(@FirstName, @MiddleInitial, @FamilyName)";
26-
2722
Models.Person person = GetFakePerson.Generate();
28-
29-
Context.Database.Instance.AddCommandBehavior(expected_cmd, cmd =>
30-
{
31-
Models.Person data = new Models.Person()
32-
{
33-
FamilyName = cmd.Parameters["@FamilyName"].Value.ToString(),
34-
FirstName = cmd.Parameters["@FirstName"].Value.ToString(),
35-
MiddleInitial = cmd.Parameters["@MiddleInitial"].Value.IsNotNull(x => x.ToString())
36-
};
37-
38-
People.Add(data);
39-
40-
data.ID = People.Count;
41-
42-
data.FullName = String.Format("{0}, {1}{2}",
43-
data.FamilyName, data.FirstName,
44-
string.IsNullOrEmpty(data.MiddleInitial?.Trim()) ? "" : $" {data.MiddleInitial}.");
45-
46-
return new[] { data }.ToDataTable();
47-
});
48-
23+
4924
Context.People.Add(person);
5025

5126
Context.ChangeTracking.SelectMany(x => x.Value).Count(x => x.IsNew).Should().Be(1);
@@ -187,33 +162,6 @@ OUTPUT INSERTED.* INTO @output
187162
new Models.Person(){ FirstName = "First_4", FamilyName = "Last_4", FullName = "First_4 Last_4" }
188163
};
189164

190-
Context.Database.Instance.AddCommandBehavior(expected_cmd, cmd =>
191-
{
192-
cmd.Parameters["@MiddleInitial"].DbType.Should().Be(DbType.AnsiString);
193-
194-
Models.Person[] _persons = new[]
195-
{
196-
new Models.Person()
197-
{
198-
FamilyName = cmd.Parameters["@FamilyName"].Value.ToString(),
199-
FirstName = cmd.Parameters["@FirstName"].Value.ToString(),
200-
MiddleInitial = cmd.Parameters["@MiddleInitial"].Value.IsNotNull(x => x.ToString())
201-
}
202-
};
203-
204-
foreach (var person in _persons)
205-
{
206-
People.Add(person);
207-
208-
person.ID = People.Count;
209-
person.FullName = String.Format("{0}, {1}{2}",
210-
person.FamilyName, person.FirstName,
211-
person.MiddleInitial.IsNotNullOrEmpty() ? $" {person.MiddleInitial}." : "");
212-
}
213-
214-
return _persons.ToDataTable();
215-
});
216-
217165
Context.People.AddRange(people);
218166

219167
Context.ChangeTracking.SelectMany(x => x.Value).Count(x => x.IsNew).Should().Be(3);
@@ -330,17 +278,6 @@ public void ShouldBeAbleToInsertRenterWithCompositeKey(bool withUDTT, string exp
330278

331279
renters.CopyTo(original, 0);
332280

333-
//if (!withUDTT)
334-
//{
335-
// // ubunto and windows format dates very differently
336-
// for (int i = 0; i < renters.Length; i++)
337-
// {
338-
// expected = expected
339-
// .Replace($"[StartDate_{i}]", renters[i].StartDate.ToString())
340-
// .Replace($"[EndDate_{i}]", renters[i].EndDate.ToString());
341-
// }
342-
//}
343-
344281
Context.Database.Instance.AddCommandBehavior(expected, cmd =>
345282
{
346283
if (withUDTT)

SubSonic.Tests/DAL/DbModels/PersonTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ public void CanInsertPersonRecords(int count)
3333
.RuleFor(p => p.FamilyName, x => x.PickRandom(DataSeed.Person.FamilyNames))
3434
.RuleFor(p => p.FirstName, x => x.PickRandom(DataSeed.Person.FirstNames))
3535
.RuleFor(p => p.MiddleInitial, x => x.PickRandom(DataSeed.Person.MiddleInitial))
36-
.RuleFor(p => p.Renters, x => new HashSet<Models.Renter>())
3736
.RuleFor(p => p.FullName, x => string.Empty);
3837

3938
Context.People.AddRange(people.Generate(count));

SubSonic.Tests/DAL/DynamicProxy/DynamicProxyTests.cs

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace SubSonic.Tests.DAL
1010
{
1111
using Extensions.Test;
1212
using Linq;
13+
using SubSonic.Collections;
1314
using SUT;
1415

1516
[TestFixture]
@@ -199,9 +200,93 @@ public void ProxyCollectionPropertyWillNotLoadWhenNotNullAndCountGreaterThanZero
199200
{
200201
RealEstateProperty instance = DynamicProxy.CreateProxyInstanceOf<RealEstateProperty>(Context);
201202

202-
instance.Units = new HashSet<Unit>(new[] { DynamicProxy.CreateProxyInstanceOf<Unit>(Context) });
203+
instance.Units = new SubSonicCollection<Unit>(new[] { DynamicProxy.CreateProxyInstanceOf<Unit>(Context) });
204+
205+
instance.Units = new SubSonicCollection<Unit>()
206+
{
207+
DynamicProxy.CreateProxyInstanceOf<Unit>(Context)
208+
};
203209

204210
instance.Units.Should().NotBeNull();
205211
}
212+
213+
[Test]
214+
public void ProxyInitializationViaContext()
215+
{
216+
RealEstateProperty instance = Context.NewEntity<RealEstateProperty>();
217+
218+
((IEntityProxy)instance).IsNew.Should().BeTrue();
219+
}
220+
221+
private static IEnumerable<RealEstateProperty> ProxyInitializationViaContextSource()
222+
{
223+
yield return null;
224+
yield return new RealEstateProperty() { ID = 0, StatusID = 1, HasParallelPowerGeneration = true };
225+
}
226+
227+
[Test]
228+
[TestCaseSource(nameof(ProxyInitializationViaContextSource))]
229+
public void ProxyInitializationViaContext(RealEstateProperty source)
230+
{
231+
RealEstateProperty instance = Context.NewEntityFrom(source);
232+
233+
((IEntityProxy)instance).IsNew.Should().BeTrue();
234+
((IEntityProxy)instance).IsDirty.Should().BeFalse();
235+
236+
IDbQuery query = null;
237+
238+
FluentActions.Invoking(() =>
239+
{
240+
if (instance.Units.Provider is ISubSonicQueryProvider provider)
241+
{
242+
query = provider.ToQuery(instance.Units.Expression);
243+
}
244+
}).Should().NotThrow();
245+
246+
query.Sql.Should().Be(@"SELECT [T1].[ID], [T1].[Bedrooms] AS [NumberOfBedrooms], [T1].[StatusID], [T1].[RealEstatePropertyID]
247+
FROM [dbo].[Unit] AS [T1]
248+
WHERE ([T1].[RealEstatePropertyID] = @realestatepropertyid_1)");
249+
query.Parameters.Get("@realestatepropertyid_1").GetValue<int>().Should().Be(instance.ID);
250+
}
251+
252+
[Test]
253+
public void ProxyInitializationViaContextSavedToDb()
254+
{
255+
Person instance = GetFakePerson.Generate();
256+
257+
((IEntityProxy)instance).IsNew.Should().BeTrue();
258+
((IEntityProxy)instance).IsDirty.Should().BeFalse();
259+
260+
IDbQuery query = null;
261+
262+
FluentActions.Invoking(() =>
263+
{
264+
if (instance.Renters.Provider is ISubSonicQueryProvider provider)
265+
{
266+
query = provider.ToQuery(instance.Renters.Expression);
267+
}
268+
}).Should().NotThrow();
269+
270+
query.Sql.Should().Be(@"SELECT [T1].[PersonID], [T1].[UnitID], [T1].[Rent], [T1].[StartDate], [T1].[EndDate]
271+
FROM [dbo].[Renter] AS [T1]
272+
WHERE ([T1].[PersonID] = @personid_1)");
273+
query.Parameters.Get("@personid_1").GetValue<int>().Should().Be(instance.ID);
274+
275+
Context.People.Add(instance);
276+
277+
Context.SaveChanges();
278+
279+
instance.ID.Should().NotBe(0);
280+
281+
FluentActions.Invoking(() =>
282+
{
283+
if (instance.Renters.Provider is ISubSonicQueryProvider provider)
284+
{
285+
query = provider.ToQuery(instance.Renters.Expression);
286+
}
287+
}).Should().NotThrow();
288+
289+
query.Parameters.Get("@personid_1").GetValue<int>().Should().Be(instance.ID);
290+
}
206291
}
207292
}

SubSonic.Tests/DAL/SUT/BaseTestFixture.cs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,20 @@ protected Bogus.Faker<Person> GetFakePerson
3030
{
3131
get
3232
{
33-
return new Bogus.Faker<Person>()
33+
return new SubSonicFaker<Person>()
34+
.UseDbContext()
3435
.RuleFor(person => person.ID, set => 0)
3536
.RuleFor(person => person.FirstName, set => set.PickRandom(DataSeed.Person.FirstNames))
3637
.RuleFor(person => person.MiddleInitial, set => set.PickRandom(DataSeed.Person.MiddleInitial))
3738
.RuleFor(person => person.FamilyName, set => set.PickRandom(DataSeed.Person.FamilyNames))
3839
.RuleFor(person => person.FullName, set => null)
39-
.RuleFor(person => person.Renters, set => new HashSet<Renter>());
40+
.FinishWith((faker, person) =>
41+
{
42+
if (person is IEntityProxy proxy)
43+
{
44+
proxy.IsDirty = false;
45+
}
46+
});
4047
}
4148
}
4249

@@ -47,6 +54,8 @@ public virtual void SetupTestFixture()
4754

4855
Context.Instance.GetService<DbProviderFactory, SubSonicMockDbClient>().ClearBehaviors();
4956

57+
SetInsertBehaviors();
58+
5059
Statuses = new List<Status>()
5160
{
5261
new Status() { ID = 1, Name = "Vacant", IsAvailableStatus = true },
@@ -88,6 +97,35 @@ public virtual void SetupTestFixture()
8897
};
8998
}
9099

100+
private void SetInsertBehaviors()
101+
{
102+
string
103+
insert_person = @"INSERT INTO [dbo].[Person]
104+
OUTPUT INSERTED.* INTO @output
105+
VALUES
106+
(@FirstName, @MiddleInitial, @FamilyName)";
107+
108+
Context.Database.Instance.AddCommandBehavior(insert_person, cmd =>
109+
{
110+
Person data = new Person()
111+
{
112+
FamilyName = cmd.Parameters["@FamilyName"].Value.ToString(),
113+
FirstName = cmd.Parameters["@FirstName"].Value.ToString(),
114+
MiddleInitial = cmd.Parameters["@MiddleInitial"].Value.IsNotNull(x => x.ToString())
115+
};
116+
117+
People.Add(data);
118+
119+
data.ID = People.Count;
120+
121+
data.FullName = String.Format("{0}, {1}{2}",
122+
data.FamilyName, data.FirstName,
123+
string.IsNullOrEmpty(data.MiddleInitial?.Trim()) ? "" : $" {data.MiddleInitial}.");
124+
125+
return new[] { data }.ToDataTable();
126+
});
127+
}
128+
91129
protected IEnumerable<Status> Statuses { get; set; }
92130
protected ICollection<Unit> Units { get; set; }
93131
protected ICollection<Renter> Renters { get; set; }
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Bogus;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Text;
5+
6+
namespace SubSonic.Tests.DAL.SUT
7+
{
8+
class SubSonicFaker<TEntity>
9+
: Faker<TEntity>
10+
where TEntity: class
11+
{
12+
public SubSonicFaker<TEntity> UseDbContext()
13+
{
14+
if (base.CustomInstantiator(f => DbContext.Current.NewEntity<TEntity>()) is SubSonicFaker<TEntity> faker)
15+
{
16+
return faker;
17+
}
18+
19+
throw Error.InvalidOperation();
20+
}
21+
}
22+
}

SubSonic/Data/DynamicProxies/DbContextAccessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public DbContextAccessor(DbContext dbContext)
2525

2626
private DbContext DbContext { get; }
2727

28-
public DbModel Model => DbContext.Model;
28+
public DbSchemaModel Model => DbContext.Model;
2929

3030
public TProperty LoadProperty<TEntity, TProperty>(TEntity entity, PropertyInfo info)
3131
where TEntity : class

0 commit comments

Comments
 (0)