Skip to content

Commit 9b94200

Browse files
committed
Improve docs with samples
1 parent 8c52c10 commit 9b94200

13 files changed

+459
-69
lines changed

DynamoDBGenerator.sln

+36
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E84C4630
1212
EndProject
1313
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{11F1D954-39EC-4EDD-9460-04FCC216E97A}"
1414
EndProject
15+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TypeSupport", "samples\TypeSupport\TypeSupport.csproj", "{89AB1F2A-52E4-4920-BAC9-93CB1B9BCA93}"
16+
EndProject
17+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{BFE1E1F9-5DCC-4C59-B308-BB2BFB94787B}"
18+
EndProject
19+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TypeSupport", "samples\TypeSupport\TypeSupport.csproj", "{29723107-5943-400C-8420-5D7E0A041A94}"
20+
EndProject
21+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RequestAndResponseObjects", "samples\RequestAndResponseObjects\RequestAndResponseObjects.csproj", "{03AFDE62-49CB-4844-838F-7E87BA6AFDA1}"
22+
EndProject
23+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeyConversion", "samples\KeyConversion\KeyConversion.csproj", "{0E92C22E-576E-4EC0-909F-B4F777A11B28}"
24+
EndProject
25+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Configuration", "samples\Configuration\Configuration.csproj", "{ABB3B374-4D5E-4DCF-9460-C7EEF4EA5A3C}"
26+
EndProject
1527
Global
1628
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1729
Debug|Any CPU = Debug|Any CPU
@@ -34,11 +46,35 @@ Global
3446
{A80AC940-3BD8-4377-BDB9-AE82FD4DF944}.Debug|Any CPU.Build.0 = Debug|Any CPU
3547
{A80AC940-3BD8-4377-BDB9-AE82FD4DF944}.Release|Any CPU.ActiveCfg = Release|Any CPU
3648
{A80AC940-3BD8-4377-BDB9-AE82FD4DF944}.Release|Any CPU.Build.0 = Release|Any CPU
49+
{89AB1F2A-52E4-4920-BAC9-93CB1B9BCA93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50+
{89AB1F2A-52E4-4920-BAC9-93CB1B9BCA93}.Debug|Any CPU.Build.0 = Debug|Any CPU
51+
{89AB1F2A-52E4-4920-BAC9-93CB1B9BCA93}.Release|Any CPU.ActiveCfg = Release|Any CPU
52+
{89AB1F2A-52E4-4920-BAC9-93CB1B9BCA93}.Release|Any CPU.Build.0 = Release|Any CPU
53+
{29723107-5943-400C-8420-5D7E0A041A94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54+
{29723107-5943-400C-8420-5D7E0A041A94}.Debug|Any CPU.Build.0 = Debug|Any CPU
55+
{29723107-5943-400C-8420-5D7E0A041A94}.Release|Any CPU.ActiveCfg = Release|Any CPU
56+
{29723107-5943-400C-8420-5D7E0A041A94}.Release|Any CPU.Build.0 = Release|Any CPU
57+
{03AFDE62-49CB-4844-838F-7E87BA6AFDA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
58+
{03AFDE62-49CB-4844-838F-7E87BA6AFDA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
59+
{03AFDE62-49CB-4844-838F-7E87BA6AFDA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
60+
{03AFDE62-49CB-4844-838F-7E87BA6AFDA1}.Release|Any CPU.Build.0 = Release|Any CPU
61+
{0E92C22E-576E-4EC0-909F-B4F777A11B28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62+
{0E92C22E-576E-4EC0-909F-B4F777A11B28}.Debug|Any CPU.Build.0 = Debug|Any CPU
63+
{0E92C22E-576E-4EC0-909F-B4F777A11B28}.Release|Any CPU.ActiveCfg = Release|Any CPU
64+
{0E92C22E-576E-4EC0-909F-B4F777A11B28}.Release|Any CPU.Build.0 = Release|Any CPU
65+
{ABB3B374-4D5E-4DCF-9460-C7EEF4EA5A3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
66+
{ABB3B374-4D5E-4DCF-9460-C7EEF4EA5A3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
67+
{ABB3B374-4D5E-4DCF-9460-C7EEF4EA5A3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
68+
{ABB3B374-4D5E-4DCF-9460-C7EEF4EA5A3C}.Release|Any CPU.Build.0 = Release|Any CPU
3769
EndGlobalSection
3870
GlobalSection(NestedProjects) = preSolution
3971
{D8EABA41-D014-49BD-B109-54829DB835E7} = {E84C4630-5241-4FAA-8F86-964AB25A2C6F}
4072
{A80AC940-3BD8-4377-BDB9-AE82FD4DF944} = {E84C4630-5241-4FAA-8F86-964AB25A2C6F}
4173
{53F899A8-28AA-450F-9C62-FD478119B2B7} = {11F1D954-39EC-4EDD-9460-04FCC216E97A}
4274
{648B1DF4-9684-4422-95F5-74BB89862E4D} = {11F1D954-39EC-4EDD-9460-04FCC216E97A}
75+
{29723107-5943-400C-8420-5D7E0A041A94} = {BFE1E1F9-5DCC-4C59-B308-BB2BFB94787B}
76+
{03AFDE62-49CB-4844-838F-7E87BA6AFDA1} = {BFE1E1F9-5DCC-4C59-B308-BB2BFB94787B}
77+
{0E92C22E-576E-4EC0-909F-B4F777A11B28} = {BFE1E1F9-5DCC-4C59-B308-BB2BFB94787B}
78+
{ABB3B374-4D5E-4DCF-9460-C7EEF4EA5A3C} = {BFE1E1F9-5DCC-4C59-B308-BB2BFB94787B}
4379
EndGlobalSection
4480
EndGlobal

README.md

+139-69
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ The source generator will look for attributes and implement interfaces that exis
4949
* Custom Converters: Create converters for your own types or override the [default converters](https://github.com/inputfalken/DynamoDB.SourceGenerator/blob/main/src/DynamoDBGenerator/Options/AttributeValueConverters.cs) built in to the library.
5050
* `ValueTuple<T>` support: You don't have to declare your own types and could instead use [tuples](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples) with custom named fields that will act as if the tuple was a type with with those data members.
5151

52-
## Conversion
52+
## Default conversion
53+
54+
If you do not override the conversion behaviour the following rules will be applied
5355

5456
### Primitive Types
5557

@@ -105,7 +107,6 @@ Types not listed above will be treated as an object by being assigned to the `M`
105107

106108
## Reference Tracker
107109

108-
109110
As part of the source generation process, two additional types will be mirrored to the provided DTO:
110111

111112
* A reference tracker that serves as attribute references on DynamoDB side.
@@ -131,12 +132,53 @@ public string MyRequiredString { get; set; }
131132
public string MyUnknownString { get; set; }
132133
```
133134

134-
## Code examples
135+
## Examples
136+
137+
### [Type support](./samples/TypeSupport)
138+
The functionality can be applied to more than classes:
139+
140+
```csharp
141+
[DynamoDBMarshaller]
142+
public partial record Record([property: DynamoDBHashKey] string Id);
143+
144+
[DynamoDBMarshaller]
145+
public partial class Class
146+
{
147+
[DynamoDBHashKey]
148+
public string Id { get; init; }
149+
}
150+
151+
[DynamoDBMarshaller]
152+
public partial struct Struct
153+
{
154+
[DynamoDBHashKey]
155+
public string Id { get; init; }
156+
}
157+
158+
[DynamoDBMarshaller]
159+
public readonly partial struct ReadOnlyStruct
160+
{
161+
[DynamoDBHashKey]
162+
public string Id { get; init; }
163+
}
164+
165+
[DynamoDBMarshaller]
166+
public readonly partial record struct ReadOnlyRecordStruct([property: DynamoDBHashKey] string Id);
167+
```
168+
169+
### [DTO sample](./samples/RequestAndResponseObjects/Person.cs)
170+
171+
An example DTO class could look like the one below.
135172

136-
An example DTO class could look like the one below. The following examples will use this sample class.
173+
**The following request examples will reuse this DTO.**
137174

138175
```csharp
139-
public class Person
176+
// A typical scenario would be that you would use multiple DynamoDBMarshaller and describe your operations via AccessName.
177+
// If you do not specify an ArgumentType it will use your main entity Type instead which is typically useful for PUT operations.
178+
[DynamoDBMarshaller]
179+
[DynamoDBMarshaller(ArgumentType = typeof((string PersonId, string Firstname)), AccessName = "UpdateFirstName")]
180+
[DynamoDBMarshaller(ArgumentType = typeof(string), AccessName = "GetById")]
181+
public partial class Person
140182
{
141183
// Will be included as 'PK' in DynamoDB.
142184
[DynamoDBHashKey("PK")]
@@ -156,69 +198,106 @@ public class Person
156198
public class Contact
157199
{
158200
// Will be included as 'Email' in DynamoDB.
159-
public string Email { get; set;}
201+
public string Email { get; set; }
160202
}
161203
}
162204
```
163205

164206
### Creating request objects
165207

166-
#### UpdateRequest without providing the DTO
208+
#### [Put request](./samples/Dto/Program.cs)
209+
210+
```csharp
211+
static PutItemRequest PutPerson()
212+
{
213+
return new PutItemRequest
214+
{
215+
TableName = "MyTable",
216+
Item = Person.PersonMarshaller.Marshall(new Person
217+
{
218+
Firstname = "John",
219+
Id = Guid.NewGuid().ToString(),
220+
ContactInfo = new Person.Contact { Email = "john@test.com" }
221+
})
222+
};
223+
}
224+
```
225+
226+
#### [Get Request & Response](./samples/RequestAndResponseObjects/Program.cs)
167227

168228
```csharp
169-
// A typical scenario would be that you would use multuple DynamoDBMarshaller and describe your operaitons via AccessName.
170-
// If you do not specify an ArgumentType it will use your main entity Type instead which is typically useful for PUT operations.
171-
[DynamoDBMarshaller(EntityType = typeof(Person), ArgumentType = typeof((string PersonId, string Firstname)), AccessName = "UpdateFirstName")]
172-
public partial class Repository { }
173229

174-
internal static class Program
230+
static GetItemRequest CreateGetItemRequest()
175231
{
176-
public static void Main()
232+
return new GetItemRequest
177233
{
178-
Repository repository = new Repository();
234+
Key = Person.GetById.PrimaryKeyMarshaller.PartitionKey("123"),
235+
TableName = "MyTable"
236+
};
237+
}
179238

180-
// Creating an AttributeExpression can be done through string interpolation where the source generator will mimic your DTO types and give you an consistent API to build the attributeExpressions.
181-
var attributeExpression = repository.UpdateFirstName.ToAttributeExpression(
182-
("personId", "John"),
183-
(dbRef, argRef) => $"{dbRef.Id} = {argRef.PersonId}", // The condition
184-
(dbRef, argRef) => $"SET {dbRef.Firstname} = {argRef.FirstName}" // The update operation
185-
);
239+
static Person DeserializeResponse(GetItemResponse response)
240+
{
241+
if (response.HttpStatusCode != HttpStatusCode.OK)
242+
throw new NotImplementedException();
243+
244+
return Person.GetById.Unmarshall(response.Item);
245+
}
186246

187-
// the index can be used to retrieve the expressions in the same order as you provide the string interpolations in the method call above.
188-
var condition = attributeExpression.Expressions[0];
189-
var update = attributeExpression.Expressions[1];
190-
var keys = repository.UpdateFirstName.PrimaryKeyMarshaller.PartitionKey("personId");
247+
```
191248

192-
var request = new UpdateItemRequest
193-
{
194-
ConditionExpression = condition,
195-
UpdateExpression = update,
196-
ExpressionAttributeNames = attributeExpression.Names,
197-
ExpressionAttributeValues = attributeExpression.Values,
198-
Key = keys,
199-
TableName = "MyTable"
200-
}
201-
}
249+
#### [Update request without providing the DTO](./samples/RequestAndResponseObjects/Program.cs)
250+
251+
```csharp
252+
static UpdateItemRequest UpdateFirstName()
253+
{
254+
// Creating an AttributeExpression can be done through string interpolation where the source generator will mimic your DTO types and give you an consistent API to build the attributeExpressions.
255+
var attributeExpression = Person.UpdateFirstName.ToAttributeExpression(
256+
("personId", "John"),
257+
(dbRef, argRef) => $"{dbRef.Id} = {argRef.PersonId}", // The condition
258+
(dbRef, argRef) => $"SET {dbRef.Firstname} = {argRef.Firstname}" // The update operation
259+
);
260+
261+
// the index can be used to retrieve the expressions in the same order as you provide the string interpolations in the method call above.
262+
var condition = attributeExpression.Expressions[0];
263+
var update = attributeExpression.Expressions[1];
264+
var keys = Person.UpdateFirstName.PrimaryKeyMarshaller.PartitionKey("personId");
265+
266+
return new UpdateItemRequest
267+
{
268+
ConditionExpression = condition,
269+
UpdateExpression = update,
270+
ExpressionAttributeNames = attributeExpression.Names,
271+
ExpressionAttributeValues = attributeExpression.Values,
272+
Key = keys,
273+
TableName = "MyTable"
274+
};
202275
}
203276
```
204277

205-
### Key conversion
278+
### [Key conversion](./samples/KeyConversion/Program.cs)
206279

207280
The key marshallers contain three methods based on your intent.
208281
The source generator will internally validate your object arguments. So if you pass a `int` but the actual key is represented as a `string`, then you will get an `exception`.
209282

210283
* `Keys(object partitionKey, object rangeKey)`
211-
* Used when you want convert both a partion key and a range key.
284+
* Used when you want convert both a partition key and a range key.
212285
* `PartionKey(object key)`
213-
* Used when you only want to only convert a partiton key without a range key.
286+
* Used when you only want to only convert a partition key without a range key.
214287
* `RangeKey(object key)`
215288
* Used when you only want to only convert a range key without a partition key.
216289

217-
218290
```csharp
291+
// PrimaryKeyMarshaller is used to convert the keys obtained from the [DynamoDBHashKey] and [DynamoDBRangeKey] attributes.
292+
var keyMarshaller = EntityDTO.KeyMarshallerSample.PrimaryKeyMarshaller;
219293

220-
[DynamoDBMarshaller(AccessName = 'KeyMarshallerSample')]
221-
public class EntityDTO
294+
// IndexKeyMarshaller requires an argument that is the index name so it can provide you with the correct conversion based on the indexes you may have.
295+
// It works the same way for both LocalSecondaryIndex and GlobalSecondaryIndex attributes.
296+
var GSIKeyMarshaller = EntityDTO.KeyMarshallerSample.IndexKeyMarshaller("GSI");
297+
var LSIKeyMarshaller = EntityDTO.KeyMarshallerSample.IndexKeyMarshaller("LSI");
298+
299+
[DynamoDBMarshaller(AccessName = "KeyMarshallerSample")]
300+
public partial class EntityDTO
222301
{
223302
[DynamoDBHashKey("PK")]
224303
public string Id { get; set; }
@@ -235,33 +314,31 @@ public class EntityDTO
235314
[DynamoDBGlobalSecondaryIndexRangeKey("GSI")]
236315
public string GlobalSecondaryIndexRangeKey { get; set; }
237316
}
238-
internal static class Program
239-
{
240-
public static void Main()
241-
{
242-
// PrimaryKeyMarshaller is used to convert the keys obtained from the [DynamoDBHashKey] and [DynamoDBRangeKey] attributes.
243-
var keyMarshaller = EntityDTO.KeyMarshallerSample.PrimaryKeyMarshaller;
244-
245-
// IndexKeyMarshaller requires an argument that is the index name so it can provide you with the correct conversion based on the indexes you may have.
246-
// It works the same way for both LocalSecondaryIndex and GlobalSecondaryIndex attributes.
247-
var GSIKeyMarshaller = EntityDTO.KeyMarshallerSample.IndexKeyMarshaller("GSI");
248-
var LSIKeyMarshaller = EntityDTO.KeyMarshallerSample.IndexKeyMarshaller("LSI");
249-
}
250-
}
251317
```
252318

253-
### Configuring the marshaller
319+
### Configuring marshalling behaviour
320+
321+
By applying the DynamoDbMarshallerOptions you're able to configure all DynamoDBMarshallers that's declared on the same type.
254322

255-
#### Custom converters
323+
#### [Custom converters](./samples/Configuration/Program.cs)
256324

257325
```csharp
258-
// Implement an converter, there's also an IReferenceTypeConverter available for ReferenceTypes.
326+
[DynamoDbMarshallerOptions(Converters = typeof(MyCustomConverters))]
327+
[DynamoDBMarshaller]
328+
public partial record OverriddenConverter([property: DynamoDBHashKey] string Id, DateTime Timestamp);
329+
259330
public class UnixEpochDateTimeConverter : IValueTypeConverter<DateTime>
260331
{
332+
public UnixEpochDateTimeConverter()
333+
{
334+
}
335+
261336
// Convert the AttributeValue into a .NET type.
262337
public DateTime? Read(AttributeValue attributeValue)
263338
{
264-
return long.TryParse(attributeValue.N, out var epoch) ? DateTimeOffset.FromUnixTimeSeconds(epoch).DateTime : null;
339+
return long.TryParse(attributeValue.N, out var epoch)
340+
? DateTimeOffset.FromUnixTimeSeconds(epoch).DateTime
341+
: null;
265342
}
266343

267344
// Convert the .NET type into an AttributeValue.
@@ -270,6 +347,7 @@ public class UnixEpochDateTimeConverter : IValueTypeConverter<DateTime>
270347
return new AttributeValue { N = new DateTimeOffset(element).ToUnixTimeSeconds().ToString() };
271348
}
272349
}
350+
273351
// Create a new Converters class
274352
// You don't have to inherit from AttributeValueConverters if you do not want to use the default converters provided.
275353
public class MyCustomConverters : AttributeValueConverters
@@ -282,23 +360,15 @@ public class MyCustomConverters : AttributeValueConverters
282360
DateTimeConverter = new UnixEpochDateTimeConverter();
283361
}
284362
// You could add more converter DataMembers as fields or properties to add your own custom conversions.
285-
286-
}
287-
288-
[DynamoDBMarshallerOptions(Converter = typeof(MyCustomConverters))]
289-
[DynamoDBMarshaller(EntityType = typeof(Person), AccessName = "PersonMarshaller")]
290-
public partial Repository
291-
{
292-
293363
}
294364
```
295365

296-
#### Enum conversion
366+
#### [Enum conversion](./samples/Configuration/EnumBehaviour.cs)
297367

298368
```csharp
299-
[DynamoDBMarshallerOptions(EnumConversion = EnumConversion.Name)]
300-
[DynamoDBMarshaller(EntityType = typeof(Person), AccessName = "PersonMarshaller")]
301-
public partial class Repository { }
369+
[DynamoDbMarshallerOptions(EnumConversion = EnumConversion.Name)]
370+
[DynamoDBMarshaller]
371+
public partial record EnumBehaviour([property: DynamoDBHashKey] string Id, DayOfWeek Enum);
302372
```
303373

304374
## Project structure
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<ProjectReference Include="..\..\src\DynamoDBGenerator.SourceGenerator\DynamoDBGenerator.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
12+
<ProjectReference Include="..\..\src\DynamoDBGenerator\DynamoDBGenerator.csproj"/>
13+
</ItemGroup>
14+
15+
</Project>
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
using Amazon.DynamoDBv2.DataModel;
2+
using DynamoDBGenerator.Attributes;
3+
using DynamoDBGenerator.Options;
4+
5+
[DynamoDbMarshallerOptions(EnumConversion = EnumConversion.Name)]
6+
[DynamoDBMarshaller]
7+
public partial record EnumBehaviour([property: DynamoDBHashKey] string Id, DayOfWeek Enum);

0 commit comments

Comments
 (0)