Skip to content

Commit 036035f

Browse files
authored
Merge pull request PHOENIXCONTACT#837 from PHOENIXCONTACT/refactor/remove-iproductpartlink
Removed IProductPartLink
2 parents 5051ba6 + 9e364b6 commit 036035f

File tree

22 files changed

+83
-92
lines changed

22 files changed

+83
-92
lines changed

docs/articles/module-products/ProductDefinition.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ The level 1 product group definition is done by creating a class used to represe
1212

1313
## Product Parts
1414

15-
In most cases it makes sense to model complex products as compositions of parts instead of one huge product. Those references to products are not represented as simple properties but with an explicit link object. This structure of [IProductPartLink](/src/Moryx.AbstractionLayer/Products/IProductPartLink.cs) was created because in many cases the reference carriers attributes of its own that are neither part of the product nor its part but rather define the relationship between them. For example a watch could have two or three needles that each indicate a respective part of the time (hours, minutes, seconds, stopwatch). The role of each needle could be a part of the needle product definition, but then it would not be possible to use a certain needle for seconds on one watch and for a stopwatch on another one. Furthermore if you think of the watch needle as an isolated product, its attributes rather include size, color and shape - but not its role in a watch. To define custom attributes for relationships between products a class derived from [ProductPartLink<TProduct>](/src/Moryx.AbstractionLayer/Products/ProductPartLink.cs) must be created and used within the product.
15+
In most cases it makes sense to model complex products as compositions of parts instead of one huge product. Those references to products are not represented as simple properties but with an explicit link object. This structure of [ProductPartLink](/src/Moryx.AbstractionLayer/Products/ProductPartLink.cs) was created because in many cases the reference carriers attributes of its own that are neither part of the product nor its part but rather define the relationship between them. For example a watch could have two or three needles that each indicate a respective part of the time (hours, minutes, seconds, stopwatch). The role of each needle could be a part of the needle product definition, but then it would not be possible to use a certain needle for seconds on one watch and for a stopwatch on another one. Furthermore if you think of the watch needle as an isolated product, its attributes rather include size, color and shape - but not its role in a watch. To define custom attributes for relationships between products a class derived from [ProductPartLink<TProduct>](/src/Moryx.AbstractionLayer/Products/ProductPartLink.cs) must be created and used within the product.
1616

1717
## Create Instance
1818

docs/articles/module-products/ProductStorage.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,13 @@ The implementation of the NeedleLinkStrategy is implemented below. The `PartCrea
7676
````cs
7777
private class NeedleLinkStrategy : LinkStrategyBase<NeedleProduct>
7878
{
79-
public override void LoadPartLink(IGenericColumns linkEntity, IProductPartLink target)
79+
public override void LoadPartLink(IGenericColumns linkEntity, ProductPartLink target)
8080
{
8181
var needleLink = (NeedlePartLink)target;
8282
needleLink.Role = (NeedleRole)linkEntity.Integer1;
8383
}
8484

85-
public override void SavePartLink(IProductPartLink source, IGenericColumns target)
85+
public override void SavePartLink(ProductPartLink source, IGenericColumns target)
8686
{
8787
var needleLink = (NeedlePartLink)source;
8888
target.Integer1 = (int)needleLink.Role;

docs/migrations/v8_to_v10.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Several interfaces have been removed to streamline the codebase and reduce compl
3636

3737
- `IProductType`: Replaced with base-class `ProductType`
3838
- `IProductInstance`: Replaced with base-class `ProductInstance`
39+
- `IProductPartLink`: Replaced with base-class `ProductPartLink`
3940
- `IConfig`: Replaced with base-class `ConfigBase`
4041
- `IDatabaseConfig`: Replaced with base-class `DatabaseConfig`
4142

src/Moryx.AbstractionLayer.Products.Endpoints/PartialSerialization.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ protected bool SimpleProp(PropertyInfo prop)
4848
// Skip reference or domain model properties
4949
var type = prop.PropertyType;
5050
if (typeof(ProductType).IsAssignableFrom(type) ||
51-
typeof(IProductPartLink).IsAssignableFrom(type) ||
52-
typeof(IEnumerable<IProductPartLink>).IsAssignableFrom(type))
51+
typeof(ProductPartLink).IsAssignableFrom(type) ||
52+
typeof(IEnumerable<ProductPartLink>).IsAssignableFrom(type))
5353
return false;
5454

5555
// Filter default properties

src/Moryx.AbstractionLayer.Products.Endpoints/ProductConverter.cs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ private void ConvertParts(ProductType productType, IEnumerable<PropertyInfo> pro
9797
{
9898
var displayName = property.GetDisplayName();
9999

100-
if (typeof(IProductPartLink).IsAssignableFrom(property.PropertyType))
100+
if (typeof(ProductPartLink).IsAssignableFrom(property.PropertyType))
101101
{
102-
var link = (IProductPartLink)property.GetValue(productType);
102+
var link = (ProductPartLink)property.GetValue(productType);
103103
var partModel = ConvertPart(link);
104104
var connector = new PartConnector
105105
{
@@ -111,9 +111,9 @@ private void ConvertParts(ProductType productType, IEnumerable<PropertyInfo> pro
111111
};
112112
connectors.Add(connector);
113113
}
114-
else if (typeof(IEnumerable<IProductPartLink>).IsAssignableFrom(property.PropertyType))
114+
else if (typeof(IEnumerable<ProductPartLink>).IsAssignableFrom(property.PropertyType))
115115
{
116-
var links = (IEnumerable<IProductPartLink>)property.GetValue(productType);
116+
var links = (IEnumerable<ProductPartLink>)property.GetValue(productType);
117117
var linkType = property.PropertyType.GetGenericArguments()[0];
118118
var connector = new PartConnector
119119
{
@@ -132,12 +132,31 @@ private void ConvertParts(ProductType productType, IEnumerable<PropertyInfo> pro
132132

133133
private static string FetchProductType(Type linkType)
134134
{
135-
var partLinkInterface = linkType.GetInterfaces().First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IProductPartLink<>));
136-
var prodType = partLinkInterface.GetGenericArguments()[0];
135+
var partLinkBase = FindGenericBaseDefinition(linkType, typeof(ProductPartLink<>));
136+
if (partLinkBase == null)
137+
{
138+
throw new InvalidOperationException($"Cannot find product type for {linkType.FullName}");
139+
}
140+
141+
var prodType = partLinkBase.GetGenericArguments()[0];
137142
return prodType.FullName;
138143
}
139144

140-
private PartModel ConvertPart(IProductPartLink link)
145+
private static Type FindGenericBaseDefinition(Type type, Type genericDefinition)
146+
{
147+
var current = type;
148+
while (current != null && current != typeof(object))
149+
{
150+
if (current.IsGenericType && current.GetGenericTypeDefinition() == genericDefinition)
151+
return current;
152+
153+
current = current.BaseType;
154+
}
155+
156+
return null;
157+
}
158+
159+
private PartModel ConvertPart(ProductPartLink link)
141160
{
142161
// No link, no DTO!
143162
if (link is null || link.Product is null)
@@ -222,7 +241,7 @@ public ProductType ConvertProductBack(ProductModel source, ProductType converted
222241
value = Activator.CreateInstance(prop.PropertyType);
223242
prop.SetValue(converted, value);
224243
}
225-
UpdateReference((IProductPartLink)value, partConnector.Parts[0]);
244+
UpdateReference((ProductPartLink)value, partConnector.Parts[0]);
226245
}
227246
else if (partConnector.Parts.Length == 0)
228247
{
@@ -236,7 +255,7 @@ public ProductType ConvertProductBack(ProductModel source, ProductType converted
236255
private void UpdateCollection(IList value, IEnumerable<PartModel> parts)
237256
{
238257
// Track which part links are still represented by the models
239-
var oldParts = new List<IProductPartLink>(value.OfType<IProductPartLink>());
258+
var oldParts = new List<ProductPartLink>(value.OfType<ProductPartLink>());
240259
// Iterate over the part models
241260
// Create or update the part links
242261
var elemType = value.GetType().GetInterfaces()
@@ -251,7 +270,7 @@ private void UpdateCollection(IList value, IEnumerable<PartModel> parts)
251270
// new partlink
252271
if (oldPartMatch == null)
253272
{
254-
oldPartMatch = (IProductPartLink)Activator.CreateInstance(elemType);
273+
oldPartMatch = (ProductPartLink)Activator.CreateInstance(elemType);
255274
oldPartMatch.Product = _productManagement.LoadType(partModel.Product.Id);
256275
value.Add(oldPartMatch);
257276
}
@@ -270,7 +289,7 @@ private void UpdateCollection(IList value, IEnumerable<PartModel> parts)
270289
value.Remove(part);
271290
}
272291

273-
private void UpdateReference(IProductPartLink value, PartModel part)
292+
private void UpdateReference(ProductPartLink value, PartModel part)
274293
{
275294
EntryConvert.UpdateInstance(value, part.Properties);
276295
value.Product = part.Product is null ? null : _productManagement.LoadType(part.Product.Id);

src/Moryx.AbstractionLayer/Processes/ProcessBindingResolverFactory.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,15 @@ protected override IBindingResolverChain AddToChain(IBindingResolverChain resolv
6363
}
6464

6565
/// <summary>
66-
/// Special resolver that can detect <see cref="IProductPartLink{T}"/> and skip the additional reference to
67-
/// <see cref="IProductPartLink{T}.Product"/>.
66+
/// Special resolver that can detect <see cref="ProductPartLink{T}"/> and skip the additional reference to
67+
/// <see cref="ProductPartLink{T}.Product"/>.
6868
/// </summary>
6969
public class PartLinkShortCut : BindingResolverBase
7070
{
7171
/// <inheritdoc />
7272
protected override object Resolve(object source)
7373
{
74-
if (!(source is IProductPartLink partLink))
74+
if (!(source is ProductPartLink partLink))
7575
{
7676
// Our object is not a part link, so we leave the chain
7777
this.Remove();

src/Moryx.AbstractionLayer/Products/IProductPartLink.cs

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,6 @@
33

44
namespace Moryx.AbstractionLayer.Products
55
{
6-
/// <summary>
7-
/// Base interface for the link
8-
/// </summary>
9-
public interface IProductPartLink : IPersistentObject
10-
{
11-
/// <summary>
12-
/// Generic access to the product of this part link
13-
/// </summary>
14-
ProductType Product { get; set; }
15-
16-
/// <summary>
17-
/// Create single product instance for this part
18-
/// </summary>
19-
ProductInstance Instantiate();
20-
}
21-
22-
/// <summary>
23-
/// API for part wrapper
24-
/// </summary>
25-
/// <typeparam name="TProduct"></typeparam>
26-
public interface IProductPartLink<TProduct> : IProductPartLink
27-
where TProduct : ProductType
28-
{
29-
/// <summary>
30-
/// Typed product of this part
31-
/// </summary>
32-
new TProduct Product { get; set; }
33-
}
34-
356
/// <summary>
367
/// Extension to instantiate instance collection from product type parts collection
378
/// </summary>
@@ -40,7 +11,7 @@ public static class PartLinkExtension
4011
/// <summary>
4112
/// Instantiate product instance collection
4213
/// </summary>
43-
public static List<TInstance> Instantiate<TInstance>(this IEnumerable<IProductPartLink> parts)
14+
public static List<TInstance> Instantiate<TInstance>(this IEnumerable<ProductPartLink> parts)
4415
where TInstance : ProductInstance
4516
{
4617
return parts.Select(p => (TInstance)p.Instantiate()).ToList();

src/Moryx.AbstractionLayer/Products/ProductInstance.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public abstract class ProductInstance : IPersistentObject
2929
/// <summary>
3030
/// Part link that created this <see cref="ProductInstance"/>. This is <value>null</value> for root instances
3131
/// </summary>
32-
public IProductPartLink PartLink { get; set; }
32+
public ProductPartLink PartLink { get; set; }
3333
}
3434

3535
/// <summary>

src/Moryx.AbstractionLayer/Products/ProductPartLink.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Moryx.AbstractionLayer.Products
66
/// <summary>
77
/// Base class that allows to assign a value to <see cref="IPersistentObject.Id"/>
88
/// </summary>
9-
public abstract class ProductPartLink : IProductPartLink
9+
public abstract class ProductPartLink : IPersistentObject
1010
{
1111
/// <summary>
1212
/// Default constructor for a new part link
@@ -53,7 +53,7 @@ public virtual ProductInstance Instantiate()
5353
/// <summary>
5454
/// Class to create generic part structure
5555
/// </summary>
56-
public class ProductPartLink<TProduct> : ProductPartLink, IProductPartLink<TProduct>
56+
public class ProductPartLink<TProduct> : ProductPartLink
5757
where TProduct : ProductType
5858
{
5959
/// <summary>

src/Moryx.AbstractionLayer/Products/ProductTypeWrapper.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ public class ProductTypeWrapper
1010
public string Identifier { get; private set; }
1111
public Func<ProductType> Constructor { get; private set; }
1212

13-
public Dictionary<string, Func<IProductPartLink>> PartLinkConstructors { get; private set; }
13+
public Dictionary<string, Func<ProductPartLink>> PartLinkConstructors { get; private set; }
1414

1515
public List<PropertyInfo> Properties { get; private set; }
1616

1717
public IEnumerable<PropertyInfo> PartLinks { get; private set; }
1818

19-
public ProductTypeWrapper(string identifier, Func<ProductType> constructor, Dictionary<string, Func<IProductPartLink>> partLinkConstructors, List<PropertyInfo> properties)
19+
public ProductTypeWrapper(string identifier, Func<ProductType> constructor, Dictionary<string, Func<ProductPartLink>> partLinkConstructors, List<PropertyInfo> properties)
2020
{
2121
Identifier = identifier;
2222
Constructor = constructor;
2323
PartLinkConstructors = partLinkConstructors;
2424
Properties = properties;
25-
PartLinks = properties.Where(p => typeof(IProductPartLink).IsAssignableFrom(p.PropertyType) || typeof(IEnumerable<IProductPartLink>).IsAssignableFrom(p.PropertyType));
25+
PartLinks = properties.Where(p => typeof(ProductPartLink).IsAssignableFrom(p.PropertyType) || typeof(IEnumerable<ProductPartLink>).IsAssignableFrom(p.PropertyType));
2626
}
2727
}
2828
}

0 commit comments

Comments
 (0)