μλ νμΈμ.
μμμ΄ λΆλͺ¨λ₯Ό μ°Έμ‘°νλ κ°μ²΄ νΈλ¦¬λ₯Ό λ§λ€κ³ μΆμ§ μμ΅λλ€.
μλ₯Ό λ€μ΄ μλμ°¨μλ νμ΄μ΄κ° μκ³ νμ΄μ΄λ μλμ°¨λ₯Ό μ°Έμ‘°ν©λλ€.
μ§κΈκΉμ§ OmitOnRecursionBehaviorλ₯Ό μ¬μ©νκ³ μ°Έμ‘°λ₯Ό μλμΌλ‘ ν λΉνμ΅λλ€. λ°λͺ¨:
``` C#
var κ³ μ μ₯μΉ = new Fixture();
Fixture.Behaviors.Add(new OmitOnRecursionBehavior());
var car = fixture.Create
foreach (μλμ°¨ νμ΄μ΄μ var.Tires)
{
νμ΄μ΄.μλμ°¨ = μλμ°¨;
}
What I wanted to do:
``` c#
fixture.Customize<Car>(composer => composer
.With(car => car.Tires, fixture.Build<Tire>().With(tire => tire.Car, car)
.CreateMany().ToList()));
λ΄κ° λν νκ³ μΆμλ κ²:
``` C#
κ°μΈ ν΄λμ€ CarCustomization : ICustomization
{
public void 컀μ€ν°λ§μ΄μ§(IFixture μ‘°λͺ
κΈ°)
{
Fixture.Behaviors.Add(new OmitOnRecursionBehavior());
Fixture.Customizations.Add(new CarBuilder());
}
}
κ°μΈ ν΄λμ€ CarBuilder : ISpecimenBuilder
{
κ³΅μ© κ°μ²΄ μμ±(κ°μ²΄ μμ², ISpecimenContext 컨ν
μ€νΈ)
{
var t = μ νμΌλ‘ μμ²;
if (t != null && t == typeof(Car))
{
//μλνμ§ μμ΅λλ€. λΉλλ μ체μ μμ‘΄ν©λλ€.
var μλμ°¨ = context.Create
foreach (μλμ°¨ νμ΄μ΄μ var.Tires)
{
νμ΄μ΄.μλμ°¨ = μλμ°¨;
}
}
return new NoSpecimen(request);
}
}
Here are the Car/Tire classes:
``` c#
public class Car
{
public string Name { get; set; }
public string Description { get; set; }
public int Doors { get; set; }
public List<Tire> Tires { get; set; }
}
public class Tire
{
public int Size { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public Car Car { get; set; }
}
μ무λ λ΄κ° μ΄κ²μ κΉλνκ² ν΄κ²°νλλ‘ λμΈ μ μμ΅λκΉ?
Tire
μλ λΆλͺ¨ Car
λν μ°Έμ‘°κ° μμ΄μΌ νλ€κ³ μκ°ν©λλ€. μν μ°Έμ‘°λ λμ κ²μΌλ‘ κ°μ£Όλ©λλ€(μ΅μν autofixtureμ μν΄).
κ·Έλ¬λ μ΄κ²μ λν λ΄κ° ν΄κ²°ν΄μΌ ν λ¬Έμ μ λλ€. λ³κ²½ν μ μλ EF λͺ¨λΈλ‘ μμ μ€μ΄λ©° μ κ±°ν μ μλ μν μ°Έμ‘°κ° μμ΅λλ€. μ§κΈμ autofixtureλ₯Ό νΌνκ³ μμ§λ§ κ°λ₯ν κ²½μ° μΌλ°μ μΌλ‘ μ΄ λ¬Έμ λ₯Ό ν΄κ²°νλ λ° μ½κ°μ λ Έλ ₯μ κΈ°μΈμ΄κ³ μΆμ΅λλ€.
Domain Driven Designμ μννλ©΄ λλΆλΆμ λ Όλ¦¬κ° λλ©μΈ κ³μΈ΅μ μκ³ μμμ λν μΌλΆ λ©μλλ λ³΄λ€ λͺ νν ꡬνμ μν΄ μ°Έμ‘°κ° νμν μ μμΌλ―λ‘ λλλ‘ νΈλ¦¬ν©λλ€.
Eric Evansλ κ·Έμ μ±
" Domain-Driven Design - Tackling Complexity in the Heart of Software"μμ λ€μκ³Ό κ°μ΄ λ§νμ΅λλ€.
μν μ°Έμ‘°λ λ
Όλ¦¬μ μΌλ‘ λ§μ μμμ μ‘΄μ¬νλ©° λλ‘λ μ€κ³μλ νμνμ§λ§ μ μ§ κ΄λ¦¬κ° κΉλ€λ‘μ΅λλ€.
λν EF λ° Fluent NHibernateμ κ°μ μΈκΈ° μλ μ§μμ± κΈ°μ μ μ¬μ©νλ©΄ 맀νμ΄ κΆμ₯λκ±°λ μ΅μν λ μ½μ΅λλ€.
λ°λΌμ νΈλ¦¬ν μ루μ μ μ°Ύλ κ²μ DDD, EF λλ Fluent NHibernateλ₯Ό μ¬μ©νλ λμ λ€λ₯Έ μ¬λλ€μκ² λμμ΄ λ κ²μ λλ€.
@ploeh μ @moodmosaic μ κ³Όκ±°μ μν μ°Έμ‘°λ₯Ό μ’μνμ§ μμΌλ©° κ°λ₯ν ν νΌνλ€κ³ λΆλͺ ν λ°νμ΅λλ€. λ΄ μ΄ν΄).
κ·Έλ¬λ λ λ€ AutoFixtureμ λν κΈ°μ¬λ₯Ό μμ©νλ κ²μΌλ‘ μλ €μ Έ μμ§λ§ μ μ§ κ΄λ¦¬ λ° μ§μ λ¬Έμ λ₯Ό μΌμΌν€μ§ μλ ν νΉλ³ν μ μ©νμ§ μλ€κ³ μκ°ν©λλ€(λ§€μ° κ³΅μ νλ€κ³ μκ°ν©λλ€).
λλ μ΄ λ¬Έμ μ λν΄ λκ°λ₯Ό μλνκ³ νκ³ μ νλ λκΈ°κ° λ§€μ° ν½λλ€(μΌλ°μ μΌλ‘ μλνλ μ μ©ν κ²μ΄ μλ€κ³ κ°μ ).
λλ μ’μ λμμΈμ μ₯λ €νλ νλ μμν¬λ₯Ό μ’μνκ³ μν μ°Έμ‘°μ λ¬Έμ λ₯Ό μ΄ν΄νμ§λ§ λλ‘λ κ·Έκ²μ΄ λ Όλ¦¬μ μΈ μΌμ λλ€. μν μ°Έμ‘°λ₯Ό κΆμ₯νμ§ μμ§λ§ AutoFixtureμ μλ¦λ€μμ ν΄μΉμ§ μλ μ루μ μ μ°Ύμ μ μμ΅λλ€.
κ·νμ λ΅λ³κ³Ό κΈμ μ μΈ νλμ κ°μ¬λ립λλ€ :+1:
μ, _λ§μ½_ μ°λ¦¬κ° μ μ ν μΌλ° μ루μ μ μ°ΎμλΌ μ μλ€λ©΄, κ·Έκ²μ AutoFixtureμ _default_ λμμ΄ μλ κ²μ λλ€.
μμ CarBuilder
(μλνμ§ μλ κ²)μμ μ€μ λ‘ νμν κ²μ΄ Postprocessor<Car>
κ²μ²λΌ 보μ
λλ€. λΉμ μ κ·Έκ²μ μλ νμ΅λκΉ?
μΌλ°μ μΌλ‘ ORM ν΄λμ€μ AutoFixtureλ₯Ό μ¬μ©νλ €λ μꡬλ λνμ΄λλ μ£Όμ μ΄λ©° @adamchesterκ° μΈκΈ
λ°λ©΄μ ORMμ 3λ μ΄μ μ¬μ©νμ§ μμκΈ° λλ¬Έμ μ΄ κΈ°λ₯μ μΆκ΅¬νλ λ° κ°μΈμ μΈ κ΄μ¬μ΄ μκΈ° λλ¬Έμ λ€λ₯Έ μ¬λμ΄ μ£Όλν΄μΌ ν©λλ€.
μν μ°Έμ‘°μ νμμ±κ³Ό κ΄λ ¨νμ¬ λλ κ·Έ κ°λ μ΄ νμ€μ μ΄λΌλ λ° λμνμ§λ§, κ·Έκ²μ΄ μννΈμ¨μ΄ λμμΈμμ κ·Έκ²μ μ¬μ©νλ κ²μ΄ μ’μ μκ°μ΄λΌλ κ²μ μλ―Ένμ§λ μμ΅λλ€. μν μ°Έμ‘°λ μ½λλ₯Ό μ¬μ©νκ³ μΆλ‘ νκΈ° ν¨μ¬ λ μ΄λ ΅κ² λ§λλλ€. νΌν μ μλ μν©μ΄λΌλ©΄ κ·Έμ λ°λΌ μ΄μμΌ νμ§λ§ IMEλ μν μ°Έμ‘°λ₯Ό νΌνλ λ°©μμΌλ‘ APIλ₯Ό _νμ_ μ€κ³ν μ μμ΅λλ€. μν μ°Έμ‘°λ₯Ό μ¬μ©νμ¬ APIλ₯Ό λ§μ§λ§μΌλ‘ λ§λ κ²μ΄ μΈμ μΈμ§ λͺ¨λ₯΄κ² μ§λ§ μλ μ μ λλ€.
κ·Έλ¬λ μ΄λ₯Ό νμλ‘ νλ ORMSλ μ΄λ»μ΅λκΉ?
ORMμ μ¬μ©νλ€λ©΄ λ μ΄μ κ°μ²΄ μ§ν₯ μ½λλ‘ μμ νμ§ μμ΅λλ€. κ·νμ μ½λλ κ°λ ₯ν μ μ°¨μ μμλ₯Ό ν¬ν¨νλ _relational_μ΄ λ κ²μ λλ€. μΈμ΄ μ ν(C# λ±)μ μμ§ λ§μμμ€.
μλ§λ μ΄ λ°©λ²μΌλ‘ DDDλ₯Ό μνν μ μμ§λ§ κ°μ²΄ μ§ν₯ DDDκ° μλ κ΄κ³ν DDDκ° λ κ²μ λλ€. Martin Fowlerκ° νΈλμμ μ€ν¬λ¦½νΈ λΌκ³ λΆλ₯΄λ κ²μΌλ‘ λλ©λλ€.
λ΅λ³ κ°μ¬ν©λλ€. κ°μ¬ν©λλ€ :+1:
PostProcessorμ λν΄ λ€μ΄λ³Έ μ μ΄ μμ΅λλ€.
λ΄ μ½λλ λ€μκ³Ό κ°μ΅λλ€.
``` C#
λ΄λΆ ν΄λμ€ DomainCustomization : ICustomization
{
public void 컀μ€ν°λ§μ΄μ§(IFixture μ‘°λͺ
κΈ°)
{
Fixture.Behaviors.Add(new OmitOnRecursionBehavior());
Fixture.Customizations.Add(
μλ‘μ΄ ν¬μ€νΈ νλ‘μΈμ
);
}
}
λ΄λΆ ν΄λμ€ CarCommand : ISpecimenCommand
{
public void Execute(κ°μ²΄ νλ³Έ, ISpecimenContext 컨ν
μ€νΈ)
{
var car = Carλ‘ νλ³Έ;
if (μλμ°¨ != null)
{
//μ¬κΈ°μ null μ°Έμ‘° μμΈ, νμ΄μ΄ μμ, μλμ°¨ μ€μ μ κ° μμ
foreach (μλμ°¨ νμ΄μ΄μ var.Tires)
{
νμ΄μ΄.μλμ°¨ = μλμ°¨;
}
}
}
}
```
λ§ν΄ νμΈλ¬λ μ μμ μ€ ν λͺ μ λλ€. κ·Έμ κ²μλ¬Ό μ€ νλμμ κ·Έλ μ νν λ΄κ° νΌνλ €κ³ νλ λΉν λλ©μΈ λͺ¨λΈμ λν΄ μ΄μΌκΈ°ν©λλ€. λ΄ μν°ν°μλ μλΉμ€(νΈλμμ μ€ν¬λ¦½νΈ)κ° μλ μμ© νλ‘κ·Έλ¨μ λλΆλΆμ λ Όλ¦¬κ° μμ΅λλ€. μν°ν°μλ λͺ¨λ λ Όλ¦¬κ° μμΌλ―λ‘ κ΄λ ¨ λ°μ΄ν°κ° νμν μ μμ΅λλ€.
λλ νμ λλ©μΈ λͺ¨λΈμ΄ μλ£λ ν μ§μμ± κ³μΈ΅μ ꡬννλ κ²μΌλ‘ λλ΄λ €κ³ λ Έλ ₯νλ―λ‘ λ°μ΄ν°λ² μ΄μ€κ° λ΄ μν°ν°λ₯Ό λ°μνλ κ²μ΄ μλλλ€.
κ·Έλ¬λ EF/EF CodeFirst λ° NHibernateμ κ°μ ORMSλ μ€κ³λ₯Ό κ΄κ³νμΌλ‘ λ§λλ μλ μ€κ³μ νμν κ²λ³΄λ€ λ λ§μ μν μ°Έμ‘°μ μν°ν°μ νμνμ§ μμ κ΄κ³λ₯Ό μμ±ν©λλ€.
μ΄ ORMSλ λ§€μ° κ°λ ₯νλ©° μ΄ "μΆν¨"μ μ λΉνν©λλ€.
λλ μ΄ λ¬Έμ μ λν ν΄κ²°μ±
μ΄ λ€λ₯Έ μ¬λλ€μκ² λμμ΄ λ μ μλλ‘ λΉλΆκ° μ΄λ¬ν κΈ°μ κ³Ό λ€λ₯Έ λ§μ κΈ°μ κ³Ό ν¨κ» μ΄μμΌ ν κ²μ
λλ€.
λ€μμ _Tires_κ° μ±μμ§ SUTκ° μμ±λκ³ κ° Tire
κ° Car
μ°Έμ‘°νλ ν΅κ³Ό ν
μ€νΈμ
λλ€.
``` C#
[μ¬μ€]
κ³΅κ° λ¬΄ν¨ ν
μ€νΈ()
{
var κ³ μ μ₯μΉ = new Fixture();
μ μ°©λ¬Ό.μ¬μ©μ μ μ
Fixture.Customizations.Add(
μλ‘μ΄ FilteringSpecimenBuilder(
μλ‘μ΄ ν¬μ€νΈ νλ‘μΈμ(
μλ‘μ΄ λ©μλ νΈμΆμ(
μλ‘μ΄ ModestConstructorQuery()),
μλ‘μ΄ CarFiller()),
μλ‘μ΄ CarSpecification()));
var car = fixture.Create<Car>();
Assert.NotEmpty(car.Tires);
Array.ForEach(car.Tires.ToArray(), x => Assert.NotNull(x.Car));
}
κ°μΈ ν΄λμ€ CarFiller : ISpecimenCommand
{
public void Execute(κ°μ²΄ νλ³Έ, ISpecimenContext 컨ν
μ€νΈ)
{
if (μνΈ == null)
μλ‘μ΄ ArgumentNullException("μν") λμ§κΈ°;
if (컨ν
μ€νΈ == null)
μλ‘μ΄ ArgumentNullException("context") λμ§κΈ°;
var car = specimen as Car;
if (car == null)
throw new ArgumentException(
"The specimen must be an instance of Car.",
"specimen");
Array.ForEach(car.GetType().GetProperties(), x =>
{
if (x.Name == "Tires")
{
var tires =
((IEnumerable<object>)context
.Resolve(new MultipleRequest(typeof(Tire))))
.Cast<Tire>().ToArray();
Array.ForEach(tires, tire => tire.Car = car);
x.SetValue(car, tires.ToList());
}
else x.SetValue(car, context.Resolve(x.PropertyType));
});
}
}
κ°μΈ ν΄λμ€ CarSpecification : IRequestSpecification
{
public bool IsSatisfiedBy(κ°μ²΄ μμ²)
{
var requestType = μ νμΌλ‘ μμ²;
if (μμ² μ ν == null)
κ±°μ§μ λ°νν©λλ€.
return typeof(Car).IsAssignableFrom(requestType);
}
}
```
λ¬μ½€ν! @moodmosaic κ°μ¬ν©λλ€. μ΄ νΌμ¦μμ λͺ μ₯μ μ¬μ§μ΄ λλ½λμμ΅λλ€.
μλ¦λ€μ΄ μ½λ :)
λν μ견μ μ£Όμ @adamchester μ @ploehμκ²λ κ°μ¬λ립λλ€.
μ΄ λ¬Έμ λ₯Ό μ’ λ£νκ² μ΅λλ€. μ΄ μ λλ©΄ μΆ©λΆν©λλ€. λ³΄λ€ μΌλ°μ μΈ μ루μ μ λ§λ€λ €λ©΄ μ΄κ²μ λ€μ μ΄ μ μμ΅λλ€.
@moodmosaicμμ μ 곡νλ μν μ½λλ κ΄μ©μ μ΄λ―λ‘ νΉμ λ¬Έμ μ λν νΉμ μ루μ μΌλ‘ λμκ°μΌ ν©λλ€.
DDDμ ORMμ μ¬μ©νλ κ²μ΄ μ μ νλ€λ μ μ λν΄μλ λμνμ§ μμ΅λλ€. ORMμ κ°λ ₯ν΄ λ³΄μ΄μ§λ§ μ€μ λ‘λ λ§€μ° μ νμ μ λλ€. λ¬Έμ λ₯Ό ν΄κ²°νλ κ²μ²λΌ 보μ΄μ§λ§ λ λ§μ λ¬Έμ λ₯Ό λ§λλλ€.
DDDμ κ΄λ ¨νμ¬ λΈλ£¨ λΆμμ μ€λͺ νλ κ°μ₯ μ€μν μ μ ν¨ν΄ μ€ νλλ _Aggregate Root_μ κ°λ μ λλ€. λ΄κ° λ³Έ λͺ¨λ ORMμ μ΄ ν¨ν΄μ λ Έκ³¨μ μΌλ‘ μλ°ν©λλ€. μ§μ λ ν μ΄λΈμμ μμνμ¬ ν΄λΉ ν μ΄λΈμ ν λ‘λλ₯Ό μμν λ€μ λ€μν 'νμ μμ±'μ μ¬μ©νμ¬ μ°κ²°λ ν μ΄λΈμμ λ°μ΄ν°λ₯Ό λ‘λν μ μκΈ° λλ¬Έμ λλ€. μ΄λ 루νΈκ° μμΌλ―λ‘ λ°μ΄ν°μ λν λͺ νν μμ κΆμ΄ μμμ μλ―Έν©λλ€.
μ¬κΈ°μ λͺ¨λ κ²μ΄ λ³κ²½ κ°λ₯νλ€λ μ μ μΆκ°νλ©΄ 곧 μμ€ν μ κ°κ² λ κ²μ λλ€. μ΄λ μΆλ‘ νκΈ° λ§€μ° μ΄λ ΅μ΅λλ€.
νμ¬ ORMμΌλ‘ μ§μ ν μ§κ³ 루νΈλ₯Ό λ§λλ κ²μ λΆκ°λ₯ν©λλ€. μ§κ³ 루νΈλ λ°μ΄ν°λ₯Ό λ³κ²½ν μ μλ μ μΌν 루νΈμ¬μΌ ν©λλ€...
ORMμ λ§€κ° λ³μκ° μλ μμ±μ, κ° μμ±μ λν μ€μ μ λ° νμ μμ± κ³΅κ°μ κ°μ μ μ½ μ‘°κ±΄μ λ§λλλ€. μ΄κ²μ λλ₯Ό λ§€μ° μ§μ¦λκ² νλ€.
νμ¬ μ κ·Ό λ°©μμΌλ‘λ λ΄ λΌμ΄λΈλ¬λ¦¬μ μ¬μ©μκ° μ§κ³ 루νΈλ₯Ό ν΅ν΄ λ°μ΄ν°λ₯Ό λ³κ²½νλ€λ μ¬μ€μ λ―Ώμ μ μμ΅λλ€. Aggregate Rootκ° μ΄κ²μ μνν μ μλλΌλ λ°μ΄ν°λ² μ΄μ€μμ μ§μ μ μΈ SQL μ λ°μ΄νΈλ₯Ό λ°©μ§νμ§ λͺ»ν κ²μ λλ€...
SqlServer λ° κ΄κ³ν λ°μ΄ν°λ² μ΄μ€μ κ°ν κ²μ λ€λ₯Έ λ°©λ²μ μ°Ύλ λ° λμμ΄ λμ§ μμμ΅λλ€. λ λμ λ°©λ²μ κ³μ μ°ΎμμΌ ν μλ μμ΅λλ€.
κ·ΈλΌμλ λΆκ΅¬νκ³ DDDλ‘ λ§λ μμ© νλ‘κ·Έλ¨μ΄ λ μ μ°νκ² λ³κ²½νκ³ μ μ§ κ΄λ¦¬ν μ μλ€κ³ μκ°ν©λλ€. κ·Έ μ€ μΌλΆλ TDDμ κ²½νμ λμμ΄ λ μλ μμ΅λλ€.
μκ°ν΄μ£Όμ μ κ°μ¬ν©λλ€ :+1:
νλ¬μ μν΄ μ²λ¦¬λλ μ νμ λν .Customize νΈμΆμ΄ μλ κ²½μ° νλ¬κ° 무μλλλ‘ νλ κ²μΌλ‘ λνλ¬μ΅λλ€. μ: fixture.Customize<Car>(c => c.Without(x => x.Doors))
λλ μ΄κ²μ λ€μκ³Ό κ°μ΄ κ²°ν©νλ €κ³ λ Έλ ₯νλ€.
var fixture = new Fixture();
fixture.Customize<Tire>(c => c.Without(x => x.Car));
///fixture.Customize<Car>(c => c.Without(x => x.Doors));
fixture.Customizations.Add(
new FilteringSpecimenBuilder(
new Postprocessor(
SpecimenBuilderNodeFactory.CreateComposer<Car>().WithAutoProperties(!fixture.OmitAutoProperties)
.With(x=>x.Description, "Wee")
,
new CarFiller()),
new CarSpecification()));
var car = fixture.Create<Car>();
Assert.Equal("Wee", car.Description);
Assert.NotEmpty(car.Tires);
Array.ForEach(car.Tires.ToArray(), x => Assert.NotNull(x.Car));
Array.ForEach(car.Tires.ToArray(), x => Assert.True(x.Car == car));
λ°λΌμ μ€λͺ μ λν μ΄μ€μ μ΄ μ€ν¨ν©λλ€. μ¬μ ν μλ μμ±λκ³ μ¬μ©μ μ μλ 무μλ©λλ€. λμ κΈ΄μ₯λ μ΄ν΄λ₯Ό λ°νμΌλ‘ νλ¬μ μ λ¬λ μνΈμ΄ Composerλ₯Ό μ¬μ©νμ¬ μμ±λ κ²μΌλ‘ μμνμ κ²μ λλ€.
νΈμ§ μ΄μ else x.SetValue(car, context.Resolve(x.PropertyType))
κ° λ€λ₯Έ μμ±μ μλμΌλ‘ μμ±νκ³ μμμ μ μ μμ΅λλ€. 건배
λ μΌλ°νλ λ²μ μ λ€μκ³Ό κ°μ΅λλ€.
private class CollectionBackReferenceFiller<T, TElement> : ISpecimenCommand
where T : class
{
private readonly string _collectionProperty;
private readonly Action<TElement, T> _backReferenceSetter;
public CollectionBackReferenceFiller(string collectionProperty, Action<TElement, T> backReferenceSetter)
{
_collectionProperty = collectionProperty;
_backReferenceSetter = backReferenceSetter;
}
public void Execute(object specimen, ISpecimenContext context)
{
if (specimen == null)
throw new ArgumentNullException("specimen");
if (context == null)
throw new ArgumentNullException("context");
var typedSpecimen = specimen as T;
if (typedSpecimen == null)
throw new ArgumentException(
"The specimen must be an instance of " + typeof(T).Name,
"specimen");
Array.ForEach(typedSpecimen.GetType().GetProperties(), x =>
{
if (x.Name == _collectionProperty)
{
var elements =
((IEnumerable<object>)context
.Resolve(new MultipleRequest(typeof(TElement))))
.Cast<TElement>().ToArray();
Array.ForEach(elements, e => _backReferenceSetter(e, typedSpecimen));
x.SetValue(typedSpecimen, elements.ToList());
}
});
}
}
private class TypeSpecification<T> : IRequestSpecification
{
public bool IsSatisfiedBy(object request)
{
var requestType = request as Type;
if (requestType == null)
return false;
return typeof(T).IsAssignableFrom(requestType);
}
}
κ·νμ μΌλ° μ루μ
μ νλ₯νμ΅λλ€!
λ³΄λ€ μ νμ΄ μμ ν μ½λμ λν΄ μμ± μμ μ¬μ©νλλ‘ μ
λ°μ΄νΈνκ³ μΌλ° μ¬μ©μ μ§μ μ λ§λ€μμ΅λλ€. κ²°κ΅ μ¬μ©νμ§ μκ² λμμ§λ§ μ΄λ―Έ μμ±νκΈ° λλ¬Έμ 곡μ ν μκ°μ
λλ€. :-)
μμ. CashRegisterμλ μμμ¦μ΄ μλ λͺ©λ‘μ΄ μμ΅λλ€. μμμ¦μλ CashRegisterμ λν μ μ°Έμ‘°κ° μμ΅λλ€.
public class CashRegister : EntityBase
{
public List<Receipt> Receipts { get; set; }
}
public class Receipt : EntityBase
{
public CashRegister CashRegister { get; set; }
}
μ¬μ©μ μ μ μ¬μ©:
new BackReferenceCustomization<CashRegister, Receipt>(cashRegister => cashRegister.Receipts, receipt => receipt.CashRegister)
μ¬μ©μ μ μ:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using AutoFixture;
using AutoFixture.Kernel;
public class BackReferenceCustomization<TTypeWithList, TTypeWithBackReference> : ICustomization where TTypeWithList : class
{
private readonly Expression<Func<TTypeWithList, List<TTypeWithBackReference>>> _collectionPropertyExpression;
private readonly Expression<Func<TTypeWithBackReference, TTypeWithList>> _backReferencePropertyExpression;
public BackReferenceCustomization(
Expression<Func<TTypeWithList, List<TTypeWithBackReference>>> collectionPropertyExpression,
Expression<Func<TTypeWithBackReference, TTypeWithList>> backReferencePropertyExpression)
{
_collectionPropertyExpression = collectionPropertyExpression;
_backReferencePropertyExpression = backReferencePropertyExpression;
}
public void Customize(IFixture fixture)
{
fixture.Customize<TTypeWithBackReference>(c => c.Without(_backReferencePropertyExpression));
fixture.Customizations.Add(
new FilteringSpecimenBuilder(
new Postprocessor(
new MethodInvoker(new ModestConstructorQuery()),
new CollectionBackReferenceFiller<TTypeWithList, TTypeWithBackReference>(_collectionPropertyExpression, _backReferencePropertyExpression)
),
new TypeSpecification<TTypeWithList>()
)
);
}
}
public class CollectionBackReferenceFiller<TTypeWithList, TTypeWithBackReference> : ISpecimenCommand where TTypeWithList : class
{
private readonly Expression<Func<TTypeWithList, List<TTypeWithBackReference>>> _collectionPropertyExpression;
private readonly Expression<Func<TTypeWithBackReference, TTypeWithList>> _backReferencePropertyExpression;
public CollectionBackReferenceFiller(Expression<Func<TTypeWithList, List<TTypeWithBackReference>>> collectionPropertyExpression, Expression<Func<TTypeWithBackReference, TTypeWithList>> backReferencePropertyExpression)
{
_collectionPropertyExpression = collectionPropertyExpression;
_backReferencePropertyExpression = backReferencePropertyExpression;
}
public void Execute(object specimen, ISpecimenContext context)
{
if (specimen == null)
throw new ArgumentNullException(nameof(specimen));
if (context == null)
throw new ArgumentNullException(nameof(context));
if (!(specimen is TTypeWithList typedSpecimen))
throw new ArgumentException(
"The specimen must be an instance of " + typeof(TTypeWithList).Name,
nameof(specimen));
var elements =
((IEnumerable<object>)context
.Resolve(new MultipleRequest(typeof(TTypeWithBackReference))))
.Cast<TTypeWithBackReference>().ToList();
var collectionProperty = (PropertyInfo)((MemberExpression)_collectionPropertyExpression.Body).Member;
collectionProperty.SetValue(typedSpecimen, elements);
var backReferenceProperty = (PropertyInfo)((MemberExpression)_backReferencePropertyExpression.Body).Member;
foreach (var element in elements)
{
backReferenceProperty.SetValue(element, typedSpecimen);
}
var otherProperties = typedSpecimen.GetType().GetProperties().Where(p => !p.Equals(collectionProperty) && p.SetMethod != null);
foreach (var property in otherProperties)
{
property.SetValue(typedSpecimen, context.Resolve(property.PropertyType));
}
}
}
public class TypeSpecification<T> : IRequestSpecification
{
public bool IsSatisfiedBy(object request)
{
var requestType = request as Type;
if (requestType == null)
return false;
return typeof(T).IsAssignableFrom(requestType);
}
}
κ°μ₯ μ μ©ν λκΈ
@moodmosaicμμ μ 곡νλ μν μ½λλ κ΄μ©μ μ΄λ―λ‘ νΉμ λ¬Έμ μ λν νΉμ μ루μ μΌλ‘ λμκ°μΌ ν©λλ€.
DDDμ ORMμ μ¬μ©νλ κ²μ΄ μ μ νλ€λ μ μ λν΄μλ λμνμ§ μμ΅λλ€. ORMμ κ°λ ₯ν΄ λ³΄μ΄μ§λ§ μ€μ λ‘λ λ§€μ° μ νμ μ λλ€. λ¬Έμ λ₯Ό ν΄κ²°νλ κ²μ²λΌ 보μ΄μ§λ§ λ λ§μ λ¬Έμ λ₯Ό λ§λλλ€.
DDDμ κ΄λ ¨νμ¬ λΈλ£¨ λΆμμ μ€λͺ νλ κ°μ₯ μ€μν μ μ ν¨ν΄ μ€ νλλ _Aggregate Root_μ κ°λ μ λλ€. λ΄κ° λ³Έ λͺ¨λ ORMμ μ΄ ν¨ν΄μ λ Έκ³¨μ μΌλ‘ μλ°ν©λλ€. μ§μ λ ν μ΄λΈμμ μμνμ¬ ν΄λΉ ν μ΄λΈμ ν λ‘λλ₯Ό μμν λ€μ λ€μν 'νμ μμ±'μ μ¬μ©νμ¬ μ°κ²°λ ν μ΄λΈμμ λ°μ΄ν°λ₯Ό λ‘λν μ μκΈ° λλ¬Έμ λλ€. μ΄λ 루νΈκ° μμΌλ―λ‘ λ°μ΄ν°μ λν λͺ νν μμ κΆμ΄ μμμ μλ―Έν©λλ€.
μ¬κΈ°μ λͺ¨λ κ²μ΄ λ³κ²½ κ°λ₯νλ€λ μ μ μΆκ°νλ©΄ 곧 μμ€ν μ κ°κ² λ κ²μ λλ€. μ΄λ μΆλ‘ νκΈ° λ§€μ° μ΄λ ΅μ΅λλ€.