Skip to content

Commit fe8e6d3

Browse files
committed
Add v4.0.22 Release Notes
1 parent f132095 commit fe8e6d3

File tree

1 file changed

+254
-0
lines changed

1 file changed

+254
-0
lines changed

release-notes.md

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,259 @@
11
# Release Notes
22

3+
# v4.0.22 Release Notes
4+
5+
## OrmLite
6+
7+
This was primarily an OrmLite-focused release with the introduction of major new features:
8+
9+
### Typed SQL Expressions now support Joins!
10+
11+
Another [highly requested feature](http://servicestack.uservoice.com/forums/176786-feature-requests/suggestions/4459040-enhance-ormlite-with-common-data-usage-patterns) has been realized in this release with OrmLite's typed SqlExpressions extended to add support for Joins.
12+
13+
The new JOIN support follows OrmLite's traditional approach of a providing a DRY, typed RDBMS-agnostic wrapper that retains a high affinity with SQL, providing an intuitive API that generates predictable SQL and a light-weight mapping to clean POCO's.
14+
15+
### Basic Example
16+
17+
Starting with the most basic example you can simply specify the table you want to join with:
18+
19+
```csharp
20+
var dbCustomers = db.Select<Customer>(q => q.Join<CustomerAddress>());
21+
```
22+
23+
This query rougly maps to the following SQL:
24+
25+
```sql
26+
SELECT Customer.*
27+
FROM Customer
28+
INNER JOIN
29+
CustomerAddress ON (Customer.Id == CustomerAddress.Id)
30+
```
31+
32+
Just like before `q` is an instance of `SqlExpression<Customer>` which is bounded to the base `Customer` type (and what any subsequent implicit API's apply to).
33+
34+
To better illustrate the above query, lets expand it to the equivalent explicit query:
35+
36+
```csharp
37+
SqlExpression<Customer> q = db.From<Customer>();
38+
q.Join<Customer,CustomerAddress>();
39+
40+
List<Customer> dbCustomers = db.Select(q);
41+
```
42+
43+
### Reference Conventions
44+
45+
The above query joins together the `Customer` and `CustomerAddress` POCO's using the same relationship convention used in [OrmLite's support for References](https://github.com/ServiceStack/ServiceStack.OrmLite/blob/master/tests/ServiceStack.OrmLite.Tests/LoadReferencesTests.cs), i.e. using the referenced table `{ParentType}Id` property convention.
46+
47+
An example of what this looks like can be seen the POCO's below:
48+
49+
```csharp
50+
class Customer {
51+
public Id { get; set; }
52+
...
53+
}
54+
class CustomerAddress {
55+
public Id { get; set; }
56+
public CustomerId { get; set; } // Reference based on Property name convention
57+
}
58+
```
59+
60+
References based on matching alias names is also supported, e.g:
61+
62+
```csharp
63+
[Alias("LegacyCustomer")]
64+
class Customer {
65+
public Id { get; set; }
66+
...
67+
}
68+
class CustomerAddress {
69+
public Id { get; set; }
70+
71+
[Alias("LegacyCustomerId")] // Matches `LegacyCustomer` Alias
72+
public RenamedCustomerId { get; set; } // Reference based on Alias Convention
73+
}
74+
```
75+
76+
Either convention lets you save a POCO and all its entity references with `db.Save()`, e.g:
77+
78+
```csharp
79+
var customer = new Customer {
80+
Name = "Customer 1",
81+
PrimaryAddress = new CustomerAddress {
82+
AddressLine1 = "1 Australia Street",
83+
Country = "Australia"
84+
},
85+
};
86+
db.Save(customer, references:true);
87+
```
88+
89+
Going back to the above example:
90+
91+
```csharp
92+
q.Join<CustomerAddress>();
93+
```
94+
95+
Uses the implicit join in the above reference convention to expand into the equivalent explicit API:
96+
97+
```csharp
98+
q.Join<Customer,CustomerAddress>((customer,address) => customer.Id == address.CustomerId);
99+
```
100+
101+
### Selecting multiple columns across joined tables
102+
103+
Another behaviour implicit when selecting from a typed SqlExpression is that results are mapped to the `Customer` POCO. To change this default we just need to explicitly specify what POCO it should map to instead:
104+
105+
```csharp
106+
List<FullCustomerInfo> customers = db.Select<FullCustomerInfo>(
107+
db.From<Customer>().Join<CustomerAddress>());
108+
```
109+
110+
Where `FullCustomerInfo` is any POCO that contains a combination of properties matching any of the joined tables in the query.
111+
112+
The above example is also equivalent to the shorthand `db.Select<Into,From>()` API:
113+
114+
```csharp
115+
var customers = db.Select<FullCustomerInfo,Customer>(q => q.Join<CustomerAddress>());
116+
```
117+
118+
Rules for how results are mapped is simply each property on `FullCustomerInfo` is mapped to the first matching property in any of the tables in the order they were added to the SqlExpression.
119+
120+
As most OrmLite tables have a primary key property named `Id`, the auto-mapping includes a fallback for mapping to a full namespaced Id property in the same `{Type}Id` format. This allows you to auto-populate `CustomerId`, `CustomerAddressId` and `OrderId` columns even though they aren't a match to any of the fields in any of the joined tables.
121+
122+
### Advanced Example
123+
124+
Seeing how the SqlExpression is constructed, joined and mapped, we can take a look at a more advanced example to showcase more of the new API's available:
125+
126+
```csharp
127+
List<FullCustomerInfo> rows = db.Select<FullCustomerInfo>( // Map results to FullCustomerInfo POCO
128+
db.From<Customer>() // Create typed Customer SqlExpression
129+
.LeftJoin<CustomerAddress>() // Implict left join with base table
130+
.Join<Customer, Order>((c,o) => c.Id == o.CustomerId) // Explicit join and condition
131+
.Where(c => c.Name == "Customer 1") // Implicit condition on base table
132+
.And<Order>(o => o.Cost < 2) // Explicit condition on joined Table
133+
.Or<Customer,Order>((c,o) => c.Name == o.LineItem)); // Explicit condition with joined Tables
134+
```
135+
136+
The comments next to each line document each Type of API used. Some of the new API's introduced in this example include:
137+
138+
- Usage of `LeftJoin` for LEFT JOIN'S, `RightJoin` and `FullJoin` also available
139+
- Usage of `And<Table>()`, to specify a condition on a Joined table
140+
- Usage of `Or<Table1,Table2>`, to specify a condition against 2 joined tables
141+
142+
More code examples of References and Joined tables are available in:
143+
144+
- [LoadReferencesTests.cs](https://github.com/ServiceStack/ServiceStack.OrmLite/blob/master/tests/ServiceStack.OrmLite.Tests/LoadReferencesTests.cs)
145+
- [LoadReferencesJoinTests.cs](https://github.com/ServiceStack/ServiceStack.OrmLite/blob/master/tests/ServiceStack.OrmLite.Tests/LoadReferencesJoinTests.cs)
146+
147+
## Optimistic Concurrency
148+
149+
Another major feature added to OrmLite is support for optimistic concurrency which can be added to any table by adding a `ulong RowVersion { get; set; }` property, e.g:
150+
151+
```csharp
152+
public class Poco
153+
{
154+
...
155+
public ulong RowVersion { get; set; }
156+
}
157+
```
158+
159+
RowVersion is implemented efficiently in all major RDBMS's, i.e:
160+
161+
- Uses `rowversion` datatype in SqlServer
162+
- Uses PostgreSql's `xmin` system column (no column on table required)
163+
- Uses UPDATE triggers on MySql, Sqlite and Oracle whose lifetime is attached to Create/Drop tables APIs
164+
165+
Despite their differing implementations each provider works the same way where the `RowVersion` property is populated when the record is selected and only updates the record if the RowVersion matches with what's in the database, e.g:
166+
167+
```csharp
168+
var rowId = db.Insert(new Poco { Text = "Text" }, selectIdentity:true);
169+
170+
var row = db.SingleById<Poco>(rowId);
171+
row.Text += " Updated";
172+
db.Update(row); //success!
173+
174+
row.Text += "Attempting to update stale record";
175+
176+
//Can't update stale record
177+
Assert.Throws<OptimisticConcurrencyException>(() =>
178+
db.Update(row));
179+
180+
//Can update latest version
181+
var updatedRow = db.SingleById<Poco>(rowId); // fresh version
182+
updatedRow.Text += "Update Success!";
183+
db.Update(updatedRow);
184+
185+
updatedRow = db.SingleById<Poco>(rowId);
186+
db.Delete(updatedRow); // can delete fresh version
187+
```
188+
189+
Optimistic concurrency is only verified on API's that update or delete an entire entity, i.e. it's not enforced in partial updates. There's also an Alternative API available for DELETE's:
190+
191+
```csharp
192+
db.DeleteById<Poco>(id:updatedRow.Id, rowversion:updatedRow.RowVersion)
193+
```
194+
195+
### Other OrmLite features
196+
197+
- New [Limit API's added to JoinSqlBuilder](https://github.com/ServiceStack/ServiceStack.OrmLite/blob/master/tests/ServiceStack.OrmLite.Tests/Expression/SqlExpressionTests.cs#L126-L168)
198+
- SqlExpression's are now tied to the dialect provider at time of creation
199+
200+
## ServiceStack.Text
201+
202+
A new `JsConfig.ReuseStringBuffer` performance config option is available to JSON and JSV Text Serializers which lets you re-use ThreadStatic StringBuilder when serializing to a string. In initial benchmarks (both synchronous and parallel) it shows around a **~%30 increase in performance** for small POCO's. It can be enabled with:
203+
204+
```csharp
205+
JsConfig.ReuseStringBuffer = true;
206+
```
207+
208+
Default enum values can be excluded from being serialized with:
209+
210+
```csharp
211+
JsConfig.IncludeDefaultEnums = false;
212+
```
213+
214+
## ServiceStack
215+
216+
### [Messaging](https://github.com/ServiceStack/ServiceStack/wiki/Messaging)
217+
218+
Improved support for the MQ Request/Reply pattern with the new `GetTempQueueName()` API now available in all MQ Clients which returns a temporary queue (prefixed with `mq:tmp:`) suitable for use as the ReplyTo queue in Request/Reply scenarios:
219+
220+
```csharp
221+
mqServer.RegisterHandler<Hello>(m =>
222+
new HelloResponse { Result = "Hello, {0}!".Fmt(m.GetBody().Name) });
223+
mqServer.Start();
224+
225+
using (var mqClient = mqServer.CreateMessageQueueClient())
226+
{
227+
var replyToMq = mqClient.GetTempQueueName();
228+
mqClient.Publish(new Message<Hello>(new Hello { Name = "World" }) {
229+
ReplyTo = replyToMq
230+
});
231+
232+
IMessage<HelloResponse> responseMsg = mqClient.Get<HelloResponse>(replyToMq);
233+
mqClient.Ack(responseMsg);
234+
var responseDto = responseMsg.GetBody();
235+
}
236+
```
237+
238+
On [Rabbit MQ](https://github.com/ServiceStack/ServiceStack/wiki/Rabbit-MQ) it creates an exclusive non-durable queue.
239+
240+
In [Redis MQ](https://github.com/ServiceStack/ServiceStack/wiki/Messaging-and-Redis) there's a new `RedisMqServer.ExpireTemporaryQueues()` API which can be used on StartUp to expire temporary queues after a given period.
241+
242+
Synchronous and Parallel tests for this feature is available in [MqRequestReplyTests.cs](https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.Server.Tests/Messaging/MqRequestReplyTests.cs).
243+
244+
## New NuGet packages
245+
246+
- [ServiceStack.Authentication.LightSpeed](https://www.nuget.org/packages/ServiceStack.Authentication.LightSpeed/) is a new User Auth Repository created by [Herdy Handoko](https://plus.google.com/u/0/+HerdyHandoko/posts) providing a new persistence option for User Authentication backed by [Mindscape's LightSpeed ORM](http://www.mindscapehq.com/products/lightspeed). Checkout the [GitHub Project](https://github.com/hhandoko/ServiceStack.Authentication.LightSpeed) for more info.
247+
248+
### Other Framework Features
249+
250+
- Added support for locking users in all AuthProviders by populating `UserAuth.LockedDate`, effective from next login attempt
251+
- Reduced dependencies on all Logging providers, now only depends on `ServiceStack.Interfaces`
252+
- ContentLength is written where possible allowing [Async Progress callbacks on new payloads](https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.Endpoints.Tests/AsyncProgressTests.cs)
253+
- Non authenticated requests to `/auth` throw a 401 (otherwise returns basic session info)
254+
- Metadata filter now supports IE8/IE9
255+
- `CopyTo` and `WriteTo` Stream extensions now return bytes transferred
256+
3257
# v4.0.21 Release Notes
4258

5259
## Authentication

0 commit comments

Comments
 (0)