diff --git a/README.md b/README.md
index 8a3b47315..183a357d9 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,29 @@ The key industralisation goals are:
+## Rapid enterprise-grade API development
+
+As a result of the _Beef_ [Architecture](#Architecture), supporting [Framework](#Framework) and included [Code Generation](#Code-generation) capabilities, enterprise-grade APIs can be developed in a matter of hours, not days, in a standardised and consistent manner.
+
+The APIs created will have the following capabilities out-of-the-box with limited developer effort, so the developer can focus on the key business value:
+- Rich [Entity](./docs/Layer-Entity.md) (DTO) functionality including [INotifyPropertyChanged](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged), [IEditableObject](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.ieditableobject), [ICloneable](./src/Beef.Core/Entities/ICloneable.cs), [ICopyFrom](./src/Beef.Core/Entities/ICopyFrom.cs), [ICleanUp](./src/Beef.Core/Entities/ICleanUp.cs), [IUniueKey](./src/Beef.Core/Entities/IUniqueKey.cs), etc.
+- Rich [Reference data](./docs/Reference-Data.md) capabilities, including caching, optimised serialisation, and enriched API endpoints.
+- Rich [Validation](./docs/Beef-Validation.md) capability to simplify and ensure data integrity and consistency.
+- CRUD (Create, Read, Update and Delete) for Database ([Stored procedures](./src/Beef.Data.Database/README.md) and [Entity Framework](./src/Beef.Data.EntityFrameworkCore/README.md)), [Cosmos DB](./src/Beef.Data.Cosmos/README.md) and [OData](./src/Beef.Data.OData/README.md) in a standardised manner.
+- An approach and tooling to automate and manage [database](./tools/Beef.Database.Core/README.md) set up, configuration, and deployment.
+- [Paging](./src/Beef.Core/Entities/PagingArgs.cs) (skip and top) and resulting total count, that flows from API through to the underlying data source in a consistent and seamless manner.
+- [ETag](https://en.wikipedia.org/wiki/HTTP_ETag) (concurrency) and `If-Match`/`If-None-Match` handling.
+- JSON response field [filtering (include/exclude)](./src/Beef.Core/Json/JsonPropertyFilter.cs) to minimise resulting payload size (e.g. `$fields=firstname,lastname`)
+- [HTTP Patch](./docs/Http-Patch.md) support, where required, in a simplified and consistent manner.
+- An end-to-end intra-domain [integration testing](./tools/Beef.Test.NUnit/README.md) approach enables effective tests to be built easily and quickly.
+- [Event](./src/Beef.Events/README.md) publishing and subcribing to enable an event-driven architecture.
+
+To implement these included capabilities would literally take months/years to build and test; these are available for developers to use immediately, and contribute back if so inclined.
+
+To [get started](#Getting-started) a .NET Core [template capability](./templates/Beef.Template.Solution/README.md) is provided to enable you to get a solution up and running in minutes.
+
+
+
## Architecture
_Beef_ has been developed to encourage the standardisation and industrialisation of the tiering and layering within the microservices (APIs) of an Application Architecture.
@@ -140,15 +163,12 @@ Sample | Description
The following are references to additional documentation (these are all accessible via links within this and other documentation):
-### Major versions
-
-- [v3.1](./docs/Upgrade-dotnet-core-v3-1.md)
-
### General
- [Reference data](./docs/Reference-Data.md)
- [Validation](./docs/Beef-Validation.md)
- [HTTP PATCH](./docs/Http-Patch.md)
+- [Authentication](./docs/Authentication.md)
### Solution
@@ -180,6 +200,10 @@ The following are references to additional documentation (these are all accessib
- [OrderBy element](./docs/Table-OrderBy-element.md)
- [Execute element](./docs/Table-Execute-element.md)
+### Major versions
+
+- [v3.1](./docs/Upgrade-dotnet-core-v3-1.md)
+
## License
@@ -190,7 +214,7 @@ _Beef_ is open source under the [MIT license](./LICENSE) and is free for commerc
## Getting started
-To start using _Beef_ you do not need to clone the repo; you just need to create a solution with the underlying projects using the prescribed [solution structure](./docs/Solution-Structure.md), including referencing the appropriate NuGet packages. To accelerate this a .NET Core [template capability](./templates/Beef.Template.Solution/README.md) is provided to enable you to get up and running in minutes.
+To start using _Beef_ you do not need to clone or fork the repo; you just need to create a solution with the underlying projects using the prescribed [solution structure](./docs/Solution-Structure.md), including referencing the appropriate [NuGet packages](#Framework). To accelerate this a .NET Core [template capability](./templates/Beef.Template.Solution/README.md) is provided to enable you to get up and running in minutes.
See the following for example end-to-end usage; each demonstrating the same API functionality leveraging different data sources to accomplish:
- [Cosmos sample](./docs/Sample-Cosmos-GettingStarted.md)
diff --git a/docs/Authentication.md b/docs/Authentication.md
new file mode 100644
index 000000000..25748b0b0
--- /dev/null
+++ b/docs/Authentication.md
@@ -0,0 +1,223 @@
+# Authentication
+
+This article will describe how to integrate _authentication_ into a _Beef_ solution; in which _Beef_ in an of itself does not enable directly, but leverages the capabilities such as [Azure Active Directory B2C](https://azure.microsoft.com/en-us/services/active-directory-b2c/) to perform. Equally, it could be any _Identity platform_ of your choosing.
+
+For the purposes of this artice _AAD B2C_ will be used. Review Microsoft's [documentation](https://docs.microsoft.com/en-us/azure/active-directory-b2c/) on how to set up and configure in [_Azure_](https://portal.azure.com/) as this will not be covered here.
+
+
+
+## Company.AppName.Api
+
+The _authentication_ process primarily takes place within the API itself. This capability is added within the `Startup.cs` leveraging the standard _ASP.NET Core_ [authentication](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication/) capabilities, as further described.
+
+
+
+### ConfigureServices
+
+Within the `ConfigureServices` method a call to `AddAuthentication` is required to configure. For _AAD B2C_ `AddAzureADB2CBearer` is used to load in the appropriate configuration ([`Microsoft.AspNetCore.Authentication.AzureADB2C.UI`](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.AzureADB2C.UI) NuGet package is required).
+
+``` csharp
+public void ConfigureServices(IServiceCollection services)
+{
+ // Add authentication using Azure AD B2C.
+ services.AddAuthentication(AzureADB2CDefaults.BearerAuthenticationScheme)
+ .AddAzureADB2CBearer(options => _config.Bind("AzureAdB2C", options));
+
+ ...
+```
+
+
+
+The `Bind` method loads the configuration from the application settings; for _Beef_ this is the `webapisettings.json` file. The `"AzureAdB2C"` represents the node within the underlying JSON that contains the corresponding configuration:
+
+``` json
+{
+ "AzureAdB2C": {
+ "Domain": "Xxxx.onmicrosoft.com", // Azure AD B2C domain name
+ "Instance": "https://Xxxx.b2clogin.com/tfp/", // Instance name, the domain name Xxxx is duplicated here
+ "ClientId": "12345678-097e-4786-b489-123dabeff688", // Application (client) identifier
+ "SignUpSignInPolicyId": "B2C_1_SignUpSignIn" // SignUpSignIn policy name
+ }, ...
+```
+
+
+
+#### Swagger
+
+Where [Swagger UI](https://www.nuget.org/packages/Swashbuckle.AspNetCore.SwaggerUI/) is being used and support for entering in the bearer token is required, then `AddSecurityDefinition` and `OperationFilter` are required, as follows:
+
+``` csharp
+services.AddSwaggerGen(c =>
+{
+ c.SwaggerDoc("v1", new OpenApiInfo { Title = "Xxxx API", Version = "v1" });
+
+ var xmlName = $"{Assembly.GetEntryAssembly()!.GetName().Name}.xml";
+ var xmlFile = Path.Combine(AppContext.BaseDirectory, xmlName);
+ if (File.Exists(xmlFile))
+ c.IncludeXmlComments(xmlFile);
+
+ c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
+ {
+ Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: bearer {token}\"",
+ Name = "Authorization",
+ In = ParameterLocation.Header,
+ Type = SecuritySchemeType.ApiKey
+ });
+
+ c.OperationFilter();
+});
+```
+
+
+
+### Configure
+
+The next step is to add the _authentication_ into the HTTP request pipeline. The sequencing of this is important, as follows: `UseRouting`, `UseAuthentication`, `UseAuthorization`, `UseExecutionContext` and `UseEndpoints`.
+
+For _Beef_ the [`ExecutionContext`](../src/Beef.Core/ExecutionContext.cs) plays a key role for housing the user details, namely the `Username` (optionally `UserId`). For the likes of _authorization_ `SetRoles` can also be used. To enable additional capabilities a custom [`ExecutionContext`](../samples/Cdr.Banking/Cdr.Banking.Business/ExecutionContext.cs) can be created (inheriting base) similar to that demonstrated within the [Cdr.Banking](../samples/Cdr.Banking/README.md) sample.
+
+The `UseExecutionContext` also represents an opportuntity to perform further authentication validation, such as verifying the issuer (`iss`) and audience (`aud`) claims for example.
+
+_Note:_ the `Username` is set to `emails#oid` claims as the `emails` value may not be unique and is mutable, whereas the `oid` is unique and immutable. This may also be appropriate in your scenario especially where the `Username` is used for the likes of auditing.
+
+``` csharp
+public void Configure(IApplicationBuilder app, IHttpClientFactory clientFactory)
+{
+ ...
+
+ // Use routing, authentication and authorization.
+ app.UseRouting();
+ app.UseAuthentication();
+ app.UseAuthorization();
+
+ // Add execution context set up to the pipeline (must be after UseAuth* as needs claims from user).
+ app.UseExecutionContext((ctx, ec) =>
+ {
+ if (ctx.User.Identity.IsAuthenticated)
+ {
+ if (Guid.TryParse(ctx.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value, out var oid))
+ fec.UserId = oid;
+ else
+ throw new Beef.AuthenticationException("Token must have an 'oid' (object identifier GUID) claim.");
+
+ fec.Username = $"{ctx.User.FindFirst("emails")?.Value ?? throw new Beef.AuthenticationException("Token must have an 'emails' claim.")}#{fec.UserId}";
+ }
+ else
+ fec.Username = "Anonymous";
+ });
+
+ // Finally add the controllers.
+ app.UseEndpoints(endpoints => endpoints.MapControllers());
+```
+
+
+
+_Disclaimer:_ the example above is for illustrative purposes only; it is the responsibility of the developer to fully implement the claims verification that is applicable to their specific use case.
+
+
+
+## Company.AppName.CodeGen
+
+For the authentication to occur within an API invocation the [`AuthorizeAttribute`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authorization.authorizeattribute) must be specified. The output of this _attribute_ is controlled by the [code generation](../tools/Beef.CodeGen.Core/README.md) configuration.
+
+The following XML elements support the `WebApiAuthorize` attribute, with two options `Authorize` or `AllowAnonymous`. The value is inherited from its parent within the hierarchy where not explicitly defined (overridden):
+
+1. [`CodeGeneration`](./Entity-CodeGeneration-element.md)
+2. [`Entity`](./Entity-CodeGeneration-element.md)
+3. [`Operation`](./Entity-CodeGeneration-element.md)
+
+_Note:_ Where no `WebApiAuthorize` attribute is specified and cannot be inferred via parental inheritence, it will default to `AllowAnonymous`.
+
+
+
+## Company.AppName.Test
+
+To support the [intra-domain integration testing](../tools/Beef.Test.NUnit/README.md) the _bearer token_ must be passed from the test to the API otherwise all requests will fail with an authentication error.
+
+
+### FixtureSetUp
+
+The [`AgentTester`](../tools/Beef.Test.NUnit/AgentTester.cs) has a static `RegisterBeforeRequest` method that enables the [`HttpRequestMessage`](https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httprequestmessage) to be modified prior to the request being made.
+
+Refer to the Microsoft [documentation](https://docs.microsoft.com/en-us/azure/active-directory-b2c/configure-ropc) for further AAD B2C configuration and troubleshooting.
+
+The following code demonstrates the creation of the _bearer token_ by calling the _OAuth_ endpoint passing the username and password. The resulting _token_ is then added to the HTTP request header. Note that the username comes from the `ExecutionContext.Current.Username` which is set within each executing test (see next section).
+
+``` csharp
+[SetUpFixture]
+public class FixtureSetUp
+{
+ private static readonly KeyedLock _lock = new KeyedLock();
+ private static readonly Dictionary _userTokens = new Dictionary();
+
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ TestSetUp.RegisterSetUp(async (count, _) =>
+ {
+ return await DatabaseExecutor.RunAsync(
+ count == 0 ? DatabaseExecutorCommand.ResetAndDatabase : DatabaseExecutorCommand.ResetAndData,
+ AgentTester.Configuration["ConnectionStrings:Database"],
+ typeof(DatabaseExecutor).Assembly, typeof(Database.Program).Assembly, Assembly.GetExecutingAssembly()).ConfigureAwait(false) == 0;
+ });
+
+ AgentTester.StartupTestServer(environmentVariablesPrefix: "AppName_");
+ AgentTester.DefaultExpectNoEvents = true;
+ AgentTester.RegisterBeforeRequest(BeforeRequet);
+ }
+
+ private static void BeforeRequet(HttpRequestMessage r)
+ {
+ var username = ExecutionContext.Current.Username;
+ if (username.Equals("Anonymous", System.StringComparison.OrdinalIgnoreCase))
+ return;
+
+ // Cache the token for a user to minimise web calls (perf improvement).
+ _lock.Lock(username, () =>
+ {
+ if (!_userTokens.TryGetValue(username, out string? token))
+ {
+ var data = new NameValueCollection
+ {
+ { "grant_type", "password" },
+ { "client_id", "12345678-097e-4786-b489-123dabeff688" }, // Application (client) identifier
+ { "scope", $"openid 12345678-097e-4786-b489-123dabeff688 offline_access" },
+ { "username", $"{username}@domain.com" }, // Appends domain to user (if applicable)
+ { "password", "password" } // Assumes all test users have same password
+ };
+
+ // The 'Xxxx' represents your AAD B2C domain
+ using var webClient = new WebClient();
+ var bytes = webClient.UploadValues("https://Xxxx.b2clogin.com/Xxxx.onmicrosoft.com/oauth2/v2.0/token?p=B2C_1_ROPC_Auth", "POST", data);
+ var body = Encoding.UTF8.GetString(bytes);
+ token = (string)JObject.Parse(body)["access_token"]!;
+ _userTokens.Add(username, token);
+ }
+
+ r.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
+ });
+ }
+}
+```
+
+
+
+### Tests
+
+The username can be specified for each test, either using the [`TestSetUpAttribute`](../tools/Beef.Test.NUnit/TestSetUpAttribute.cs) or specifiying when invoking the [AgentTester.Create](../tools/Beef.Test.NUnit/AgentTester.cs). This in turn will set the `ExecutionContext.Current.Username`.
+
+``` csharp
+[Test, TestSetUp("username")]
+public void A110_GetMe_NotFound()
+{
+ AgentTester.Create()
+ .ExpectStatusCode(HttpStatusCode.NotFound)
+ .ExpectErrorType(Beef.ErrorType.NotFoundError)
+ .Run((a) => a.Agent.GetMeAsync());
+
+ AgentTester.Create("username2")
+ .ExpectStatusCode(HttpStatusCode.NotFound)
+ .ExpectErrorType(Beef.ErrorType.NotFoundError)
+ .Run((a) => a.Agent.GetMeAsync());
+}
+```
\ No newline at end of file
diff --git a/docs/Layer-Entity.md b/docs/Layer-Entity.md
index 6ed9eb92d..43d5fef8e 100644
--- a/docs/Layer-Entity.md
+++ b/docs/Layer-Entity.md
@@ -1,8 +1,8 @@
# Entity (DTO)
-The *Entity* ([DTO](https://en.wikipedia.org/wiki/Data_transfer_object)) is primarily responsible for defining the domain-based data model (and contract where accessible externally over-the-wire). They are also used to define the [reference data](./Reference-Data.md) model.
+The *Entity* ([DTO](https://en.wikipedia.org/wiki/Data_transfer_object)) is primarily responsible for defining the domain-based model (and contract where accessible externally over-the-wire). They are also used to define the [reference data](./Reference-Data.md) model.
-The aim here is decouple this definition, where applicable, from the underlying data source. This is likely to be defined differently, with an alternate naming conversion, alternate shape/structure, etc. This also enables the data source to evolve independently of this model, as well as possibly hide additional implementaion details.
+The aim here is decouple this definition, where applicable, from the underlying data source. This is likely to be defined differently, with an alternate naming convention, alternate shape/structure, etc. This also enables the data source to evolve independently of this model, as well as possibly hide additional implementaion details.
diff --git a/docs/Layer-ServiceInterface.md b/docs/Layer-ServiceInterface.md
index 6e5718e66..a183c3269 100644
--- a/docs/Layer-ServiceInterface.md
+++ b/docs/Layer-ServiceInterface.md
@@ -40,6 +40,7 @@ There are a number of key exceptions that have a specific built in behaviour; th
Exception | Description | HTTP Status | [`ErrorType`](../src/Beef.Core/ErrorType.cs)
-|-|-|-
+[`AuthenticationException`](../src/Beef.Core/AuthenticationException.cs) | Represents an **Authentication** exception. | 401 Unauthorized | 8 AuthenticationError
[`AuthorizationException`](../src/Beef.Core/AuthorizationException.cs) | Represents an **Authorization** exception. | 403 Forbidden | 3 AuthorizationError
[`BusinessException`](../src/Beef.Core/BusinessException.cs) | Represents a **Business** exception whereby the message returned should be displayed directly to the consumer. | 400 BadRequest | 2 BusinessError
[`ConcurrencyException`](../src/Beef.Core/ConcurrencyException.cs) | Represents a data **Concurrency** exception; generally as a result of an errant [ETag](../src/Beef.Core/Entities/IETag.cs). | 412 PreconditionFailed | 4 ConcurrencyError
@@ -58,7 +59,7 @@ Feature | Description
-|-
**`ETag`** | Where the response entity implements [`IETag`](../src/Beef.Core/Entities/IETag.cs) then the response `ETag` is set. Where the response entity implements `IEnumerable` then this is iterated and where each item implements `IETag` these and the query string are hashed to generate a largely-unique `Etag` value for the response.
**`If-None-Match`** | Where an `If-None-Match` is supplied in the request the `ETag(s)` provided will be checked against the above `ETag`; where there is a match an HTTP Status Code of `304` (`NotModified`) will be returned. This will avoid the costs of content serialisation and associated network where there is no change. Note that the request to the underlying data source will still occur as no request/response caching occurs by default (with the exception of [reference data](./Reference-Data.md)).
-**Field selection** | Fields can be selected (included), or excluded, from the response to reduce the payload to a specific data set as required. The field names are those that appear within the JSON response. Sub-entity (nested) fields can be referenced using dot-notation. This is supported on any HTTP Method invocation that returns a JSON response:
- **Include**: usage of any of the following query strings `$fields`, `$includeFields`, or `include`; for example `$fields=id,code,text`.
- **Exclude**: usage of any of the following query strings `$excludeFields` or `$exclude`; for example `$exclude=address.street,quantity`
+**Field selection** | Fields can be selected (included), or excluded, from the response to reduce the payload to a specific data set as required. The field names are those that appear within the JSON response. Sub-entity (nested) fields can be referenced using dot-notation. This is supported on any HTTP Method invocation that returns a JSON response:
- **Include**: usage of any of the following query strings `$fields`, `$includeFields`, or `$include`; for example `$fields=id,code,text`.
- **Exclude**: usage of any of the following query strings `$excludeFields` or `$exclude`; for example `$exclude=address.street,quantity`
**Paging** | Paging selection is specified within the query string and is converted to a [`PagingArgs`](../src/Beef.Core/Entities/PagingArgs.cs) for operations that have been configured to support paging:
- **Page** or **Skip** - specifying the required page (using `$page` or `$pageNumber`) or rows to skip (using `$skip`); for example `$page=10` or `$skip=100`.
- **Take** - specifying the page size in rows using `$take`, `$top`, `$size` or `pageSize`; for example `$take=25`.
- **Count** - specifying the requirement to get the total row count using `$count` or `$totalCount`; for example `$count=true`.
diff --git a/docs/Upgrade-dotnet-core-v3-1.md b/docs/Upgrade-dotnet-core-v3-1.md
index ecf2e59a2..0a1fcbf1b 100644
--- a/docs/Upgrade-dotnet-core-v3-1.md
+++ b/docs/Upgrade-dotnet-core-v3-1.md
@@ -189,7 +189,7 @@ There is a minor change required, perform a find and replace: `IHostingEnvironme
#### 5.2. Startup.cs
-Within the `ConfigureServices` method we need to switch from the new out-of-the-box JSON serializer to the `Newtonsoft.Json` version as _Beef_ currently has a hard dependency on this serializer.
+Within the `ConfigureServices` method we need to switch from the new out-of-the-box JSON serializer to the `Newtonsoft.Json` version as _Beef_ currently has a hard dependency on this serializer. Nuget package `Swashbuckle.AspNetCore.Newtonsoft` is required to enable Swagger+Nuget support.
Remove the following:
@@ -199,9 +199,10 @@ public void ConfigureServices(IServiceCollection services)
services.AddMvc();
...
+}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ...
-
+{
...
// Use mvc.
@@ -219,12 +220,15 @@ public void ConfigureServices(IServiceCollection services)
...
-public void Configure(IApplicationBuilder app, IHostingEnvironment env, ...
- {
+ // Add Swagger/Newtonsoft support.
+ services.AddSwaggerGenNewtonsoftSupport();
+}
+public void Configure(IApplicationBuilder app, IHostingEnvironment env, ...
+{
...
- // Use controllers.
+ // Use routing and map controllers.
app.UseRouting();
app.UseEndpoints(endpoints =>
{
diff --git a/src/Beef.AspNetCore.WebApi/README.md b/src/Beef.AspNetCore.WebApi/README.md
index 2eeb9b9f9..6fb22bb7f 100644
--- a/src/Beef.AspNetCore.WebApi/README.md
+++ b/src/Beef.AspNetCore.WebApi/README.md
@@ -1,3 +1,5 @@
# Beef.AspNetCore.WebApi
-Under construction :-|
\ No newline at end of file
+[![NuGet version](https://badge.fury.io/nu/Beef.AspNetCore.WebApi.svg)](https://badge.fury.io/nu/Beef.AspNetCore.WebApi)
+
+The primary goal of this assembly is to support the [Service Interface](../../docs/Layer-ServiceInterface.md) layer capabilities.
\ No newline at end of file
diff --git a/src/Beef.Core/AuthenticationException.cs b/src/Beef.Core/AuthenticationException.cs
new file mode 100644
index 000000000..6fa6702e0
--- /dev/null
+++ b/src/Beef.Core/AuthenticationException.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
+
+using System;
+using System.Net;
+
+namespace Beef
+{
+ ///
+ /// Represents an Authentication exception.
+ ///
+ public class AuthenticationException : Exception, IBusinessException
+ {
+ ///
+ /// Get or sets the value.
+ ///
+ public static bool ShouldExceptionBeLogged { get; set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AuthenticationException() : this(null!) { }
+
+ ///
+ /// Initializes a new instance of the class with a specified messsage.
+ ///
+ /// The message text.
+ public AuthenticationException(string? message)
+ : base(message ?? new LText("Beef.AuthenticationException")) { }
+
+ ///
+ /// Initializes a new instance of the class with a specified messsage and inner exception.
+ ///
+ /// The message text.
+ /// The inner .
+ public AuthenticationException(string? message, Exception innerException)
+ : base(message ?? new LText("Beef.AuthenticationException"), innerException) { }
+
+ ///
+ /// Gets the (see ).
+ ///
+ public ErrorType ErrorType => ErrorType.AuthenticationError;
+
+ ///
+ /// Gets the corresponding .
+ ///
+ public HttpStatusCode StatusCode => HttpStatusCode.Unauthorized;
+
+ ///
+ /// Indicates whether the should be logged (returns the value).
+ ///
+ public bool ShouldBeLogged => ShouldExceptionBeLogged;
+ }
+}
\ No newline at end of file
diff --git a/src/Beef.Core/Beef.Core.csproj b/src/Beef.Core/Beef.Core.csproj
index 9bcedbd14..3399f09b2 100644
--- a/src/Beef.Core/Beef.Core.csproj
+++ b/src/Beef.Core/Beef.Core.csproj
@@ -3,7 +3,7 @@
netstandard2.1
Beef
- 3.1.3
+ 3.1.4
true
Beef Developers
Avanade
diff --git a/src/Beef.Core/CHANGELOG.md b/src/Beef.Core/CHANGELOG.md
index f61715ec3..4bfab775e 100644
--- a/src/Beef.Core/CHANGELOG.md
+++ b/src/Beef.Core/CHANGELOG.md
@@ -2,6 +2,10 @@
Represents the **NuGet** versions.
+## v3.1.4
+- *Enhanced:* Added `AuthenticationException` to enable standardized handling of this exception similar to the existing `AuthorizationException`. This allows for an _authentication_ exception to be thrown which in turn will result in an `HttpStatusCode.Unauthorized (401)`.
+- *Enhanced:* Added property `UserId` to `ExecutionContext`.
+
## v3.1.3
- *Enhanced:* Added _model_ representations of `ReferenceDataBase` and `ChangeLog`.
- *Enhanced:* Added `CustomConverter` to simplify process of creating converters. Added `GuidToStringConverter` and `NullableGuidToStringConverter`.
diff --git a/src/Beef.Core/ConflictException.cs b/src/Beef.Core/ConflictException.cs
index 6271a28f9..4ce4c1f9b 100644
--- a/src/Beef.Core/ConflictException.cs
+++ b/src/Beef.Core/ConflictException.cs
@@ -16,7 +16,6 @@ public class ConflictException : Exception, IBusinessException
///
public static bool ShouldExceptionBeLogged { get; set; }
-
///
/// Initializes a new instance of the class.
///
diff --git a/src/Beef.Core/ErrorType.cs b/src/Beef.Core/ErrorType.cs
index 7ecc9e98e..2868be92c 100644
--- a/src/Beef.Core/ErrorType.cs
+++ b/src/Beef.Core/ErrorType.cs
@@ -40,6 +40,11 @@ public enum ErrorType
///
/// Indicates a Duplicate error.
///
- DuplicateError = 7
+ DuplicateError = 7,
+
+ ///
+ /// Indicates an Authentication error.
+ ///
+ AuthenticationError = 8
}
}
diff --git a/src/Beef.Core/ExecutionContext.cs b/src/Beef.Core/ExecutionContext.cs
index 62c18a7e3..225ed5365 100644
--- a/src/Beef.Core/ExecutionContext.cs
+++ b/src/Beef.Core/ExecutionContext.cs
@@ -23,6 +23,7 @@ public class ExecutionContext : IETag
private static Func _get = () => _asyncLocal.Value;
private static Action _set = (ec) => _asyncLocal.Value = ec;
+ private Guid? _userId;
private string? _username;
private Guid? _tenantId;
private string? _partitionKey;
@@ -181,6 +182,22 @@ public void RegisterLogger(Action binder)
///
public OperationType OperationType { get; set; } = OperationType.Unspecified;
+ ///
+ /// Gets or sets the user identifier. This value is immutable.
+ ///
+ public Guid? UserId
+ {
+ get => _userId;
+
+ set
+ {
+ if (_userId != null && value != _userId)
+ throw new ArgumentException(ImmutableText);
+
+ _userId = value;
+ }
+ }
+
///
/// Gets or sets the username for the request. This value is immutable.
///
diff --git a/src/Beef.Core/Strings/Resources.resx b/src/Beef.Core/Strings/Resources.resx
index 63c435e3b..7e1d6aa84 100644
--- a/src/Beef.Core/Strings/Resources.resx
+++ b/src/Beef.Core/Strings/Resources.resx
@@ -123,6 +123,9 @@
{0} is required.
+
+ An authentication error occured; the credentials you provided are not valid.
+
An authorization error occurred; you are not permitted to perform this action.
diff --git a/src/Beef.Core/WebApi/WebApiAgentResult.cs b/src/Beef.Core/WebApi/WebApiAgentResult.cs
index 3d0aa7c50..25f06df9b 100644
--- a/src/Beef.Core/WebApi/WebApiAgentResult.cs
+++ b/src/Beef.Core/WebApi/WebApiAgentResult.cs
@@ -2,7 +2,6 @@
using Beef.Entities;
using Newtonsoft.Json;
-using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
@@ -100,6 +99,9 @@ public WebApiAgentResult ThrowOnError()
case Beef.ErrorType.DuplicateError:
throw new DuplicateException(ErrorMessage);
+
+ case Beef.ErrorType.AuthenticationError:
+ throw new AuthenticationException(ErrorMessage);
}
}
@@ -189,4 +191,4 @@ public T Value
return this;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Beef.Data.Database/Beef.Data.Database.csproj b/src/Beef.Data.Database/Beef.Data.Database.csproj
index ac23eb919..67a1f2a67 100644
--- a/src/Beef.Data.Database/Beef.Data.Database.csproj
+++ b/src/Beef.Data.Database/Beef.Data.Database.csproj
@@ -2,7 +2,7 @@
netstandard2.1
- 3.1.2
+ 3.1.3
false
Beef Developers
Avanade
diff --git a/src/Beef.Data.Database/CHANGELOG.md b/src/Beef.Data.Database/CHANGELOG.md
index c1eab7c23..5439e60a3 100644
--- a/src/Beef.Data.Database/CHANGELOG.md
+++ b/src/Beef.Data.Database/CHANGELOG.md
@@ -2,6 +2,9 @@
Represents the **NuGet** versions.
+## v3.1.3
+- *Enhancement:* `Database.SetSqlSessionContext` now supports the passing of a `UserId`. This will default to the `ExecutionContext.UserId`.
+
## v3.1.2
- *Enhancement:* Updated all dependent NuGet packages to their latest respective version.
diff --git a/src/Beef.Data.Database/Database.cs b/src/Beef.Data.Database/Database.cs
index 35e86cb5f..f48333f78 100644
--- a/src/Beef.Data.Database/Database.cs
+++ b/src/Beef.Data.Database/Database.cs
@@ -93,7 +93,8 @@ protected Database(string connectionString, DbProviderFactory? provider = null)
/// The username.
/// The timestamp (where null the value will default to ).
/// The tenant identifer (where null the value will not be used).
- public void SetSqlSessionContext(DbConnection dbConnection, string username, DateTime? timestamp, Guid? tenantId = null)
+ /// The unique user identifier.
+ public void SetSqlSessionContext(DbConnection dbConnection, string username, DateTime? timestamp, Guid? tenantId = null, Guid? userId = null)
{
if (dbConnection == null)
throw new ArgumentNullException(nameof(dbConnection));
@@ -125,18 +126,26 @@ public void SetSqlSessionContext(DbConnection dbConnection, string username, Dat
cmd.Parameters.Add(p);
}
+ if (userId.HasValue)
+ {
+ p = cmd.CreateParameter();
+ p.ParameterName = "@" + DatabaseColumns.SessionContextUserId;
+ p.Value = userId.Value;
+ cmd.Parameters.Add(p);
+ }
+
cmd.ExecuteNonQuery();
}
///
- /// Sets the SQL session context using the (invokes using
+ /// Sets the SQL session context using the (invokes using
/// , and ).
///
/// The .
public void SetSqlSessionContext(DbConnection dbConnection)
{
var ec = ExecutionContext.Current ?? throw new InvalidOperationException("The ExecutionContext.Current must have an instance to SetSqlSessionContext.");
- SetSqlSessionContext(dbConnection, ec.Username, ec.Timestamp, ec.TenantId);
+ SetSqlSessionContext(dbConnection, ec.Username, ec.Timestamp, ec.TenantId, ec.UserId);
}
///
@@ -146,4 +155,4 @@ public void SetSqlSessionContext(DbConnection dbConnection)
/// This is where the should be invoked; nothing is performed by default.
public virtual void OnConnectionOpen(DbConnection dbConnection) { }
}
-}
+}
\ No newline at end of file
diff --git a/src/Beef.Data.Database/DatabaseColumns.cs b/src/Beef.Data.Database/DatabaseColumns.cs
index c9b13c4dd..8e39aa574 100644
--- a/src/Beef.Data.Database/DatabaseColumns.cs
+++ b/src/Beef.Data.Database/DatabaseColumns.cs
@@ -1,6 +1,5 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
-
namespace Beef.Data.Database
{
///
@@ -59,18 +58,23 @@ public static class DatabaseColumns
public static string ReturnValueName { get; set; } = "ReturnValue";
///
- /// Gets or sets the TenantId name (use by ).
+ /// Gets or sets the TenantId name (used by ).
///
public static string SessionContextTenantId { get; set; } = "TenantId";
///
- /// Gets or sets the Username name (use by ).
+ /// Gets or sets the Username name (used by ).
///
public static string SessionContextUsername { get; set; } = "Username";
///
- /// Gets or sets the Timestamp name (use by ).
+ /// Gets or sets the Timestamp name (used by ).
///
public static string SessionContextTimestamp { get; set; } = "Timestamp";
+
+ ///
+ /// Gets or sets the UserId name (used by ).
+ ///
+ public static string SessionContextUserId { get; set; } = "UserId";
}
-}
+}
\ No newline at end of file
diff --git a/templates/Beef.Template.Solution/Beef.Template.Solution.csproj b/templates/Beef.Template.Solution/Beef.Template.Solution.csproj
index d807034fd..28c8a6233 100644
--- a/templates/Beef.Template.Solution/Beef.Template.Solution.csproj
+++ b/templates/Beef.Template.Solution/Beef.Template.Solution.csproj
@@ -2,7 +2,7 @@
netcoreapp3.1
- 3.1.3
+ 3.1.4
Beef Developers
Avanade
Business Entity Execution Framework (Beef) template solution for use with 'dotnet new'.
diff --git a/templates/Beef.Template.Solution/CHANGELOG.md b/templates/Beef.Template.Solution/CHANGELOG.md
index e731962af..0f095a382 100644
--- a/templates/Beef.Template.Solution/CHANGELOG.md
+++ b/templates/Beef.Template.Solution/CHANGELOG.md
@@ -2,9 +2,12 @@
Represents the **NuGet** versions.
+## v3.1.4
+- *Fixed:* Updated referenced *Beef* NuGet references to latest.
+
## v3.1.3
- *Fixed:* Updated referenced *Beef* NuGet references to latest.
--
+
## v3.1.2
- *Fixed:* Updated referenced *Beef* NuGet references to latest.
diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Business/Company.AppName.Business.csproj b/templates/Beef.Template.Solution/content/Company.AppName.Business/Company.AppName.Business.csproj
index 298118974..00b045b6e 100644
--- a/templates/Beef.Template.Solution/content/Company.AppName.Business/Company.AppName.Business.csproj
+++ b/templates/Beef.Template.Solution/content/Company.AppName.Business/Company.AppName.Business.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Common/Company.AppName.Common.csproj b/templates/Beef.Template.Solution/content/Company.AppName.Common/Company.AppName.Common.csproj
index 0e1e503a8..4ce2b4b7c 100644
--- a/templates/Beef.Template.Solution/content/Company.AppName.Common/Company.AppName.Common.csproj
+++ b/templates/Beef.Template.Solution/content/Company.AppName.Common/Company.AppName.Common.csproj
@@ -4,7 +4,7 @@
enable
-
+
diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Database/Company.AppName.Database.csproj b/templates/Beef.Template.Solution/content/Company.AppName.Database/Company.AppName.Database.csproj
index 197005db9..37abbbb72 100644
--- a/templates/Beef.Template.Solution/content/Company.AppName.Database/Company.AppName.Database.csproj
+++ b/templates/Beef.Template.Solution/content/Company.AppName.Database/Company.AppName.Database.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/tools/Beef.Database.Core/Beef.Database.Core.csproj b/tools/Beef.Database.Core/Beef.Database.Core.csproj
index ec0ceeb18..44c1cefb2 100644
--- a/tools/Beef.Database.Core/Beef.Database.Core.csproj
+++ b/tools/Beef.Database.Core/Beef.Database.Core.csproj
@@ -5,7 +5,7 @@
Exe
- 3.1.3
+ 3.1.4
Beef Developers
Avanade
false
diff --git a/tools/Beef.Database.Core/CHANGELOG.md b/tools/Beef.Database.Core/CHANGELOG.md
index 4b724b505..4ea2cfacc 100644
--- a/tools/Beef.Database.Core/CHANGELOG.md
+++ b/tools/Beef.Database.Core/CHANGELOG.md
@@ -2,6 +2,9 @@
Represents the **NuGet** versions.
+## v3.1.4
+- *Enhancement:* Added `fnGetUserId` and updated `spSetSessionContext` to support the new `ExecutionContext.UserId`.
+
## v3.1.3
- *Enhancement:* Updated all dependent NuGet packages to their latest respective version.
diff --git a/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetTenantId.sql b/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetTenantId.sql
index 896f233df..05a5e02cd 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetTenantId.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetTenantId.sql
@@ -1,4 +1,6 @@
-CREATE FUNCTION [dbo].[fnGetTenantId]
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
+
+CREATE FUNCTION [dbo].[fnGetTenantId]
(
@Override as uniqueidentifier = null
)
@@ -16,5 +18,4 @@ BEGIN
END
RETURN @TenantId
-END
-
+END
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetTimestamp.sql b/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetTimestamp.sql
index 2b4fe2ff9..10923cae6 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetTimestamp.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetTimestamp.sql
@@ -1,4 +1,6 @@
-CREATE FUNCTION [dbo].[fnGetTimestamp]
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
+
+CREATE FUNCTION [dbo].[fnGetTimestamp]
(
@Override as datetime = null
)
@@ -20,5 +22,4 @@ BEGIN
END
RETURN @Timestamp
-END
-
+END
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetUserId.sql b/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetUserId.sql
new file mode 100644
index 000000000..7fa6e6d38
--- /dev/null
+++ b/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetUserId.sql
@@ -0,0 +1,21 @@
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
+
+CREATE FUNCTION [dbo].[fnGetUserId]
+(
+ @Override as uniqueidentifier = null
+)
+RETURNS uniqueidentifier
+AS
+BEGIN
+ DECLARE @UserId uniqueidentifier
+ IF @Override IS NULL
+ BEGIN
+ SET @UserId = CONVERT(uniqueidentifier, SESSION_CONTEXT(N'UserId'));
+ END
+ ELSE
+ BEGIN
+ SET @UserId = @Override
+ END
+
+ RETURN @UserId
+END
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetUsername.sql b/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetUsername.sql
index e764dd672..8ea9968b4 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetUsername.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Functions/fnGetUsername.sql
@@ -1,4 +1,6 @@
-CREATE FUNCTION [dbo].[fnGetUsername]
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
+
+CREATE FUNCTION [dbo].[fnGetUsername]
(
@Override AS NVARCHAR(1024) = null
)
diff --git a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spSetSessionContext.sql b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spSetSessionContext.sql
index ae8cb5a78..27343c866 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spSetSessionContext.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spSetSessionContext.sql
@@ -1,9 +1,10 @@
-
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
CREATE PROCEDURE [dbo].[spSetSessionContext]
@Timestamp as datetime = null,
@Username as nvarchar(1024) = null,
- @TenantId as uniqueidentifier = null
+ @TenantId as uniqueidentifier = null,
+ @UserId as uniqueidentifier = null
AS
BEGIN
IF @Timestamp IS NOT NULL
@@ -20,4 +21,9 @@ BEGIN
BEGIN
EXEC sp_set_session_context 'TenantId', @TenantId, @read_only = 1;
END
-END
+
+ IF @UserId IS NOT NULL
+ BEGIN
+ EXEC sp_set_session_context 'UserId', @UserId, @read_only = 1;
+ END
+END
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowAuthorizationException.sql b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowAuthorizationException.sql
index 6fede959a..50b572454 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowAuthorizationException.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowAuthorizationException.sql
@@ -1,5 +1,4 @@
-
-
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
CREATE PROCEDURE [dbo].[spThrowAuthorizationException]
@Message NVARCHAR(2048) = NULL
@@ -7,6 +6,4 @@ AS
BEGIN
SET NOCOUNT ON;
THROW 56003, @Message, 1
-END
-
-
+END
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowBusinessException.sql b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowBusinessException.sql
index f1a84b994..7dcf3f5b8 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowBusinessException.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowBusinessException.sql
@@ -1,5 +1,4 @@
-
-
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
CREATE PROCEDURE [dbo].[spThrowBusinessException]
@Message NVARCHAR(2048) = NULL
@@ -7,6 +6,4 @@ AS
BEGIN
SET NOCOUNT ON;
THROW 56002, @Message, 1
-END
-
-
+END
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowConcurrencyException.sql b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowConcurrencyException.sql
index 503d0e9c3..e988a233b 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowConcurrencyException.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowConcurrencyException.sql
@@ -1,4 +1,4 @@
-
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
CREATE PROCEDURE [dbo].[spThrowConcurrencyException]
@Message NVARCHAR(2048) = NULL
@@ -6,5 +6,4 @@ AS
BEGIN
SET NOCOUNT ON;
THROW 56004, @Message, 1
-END
-
+END
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowConflictException.sql b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowConflictException.sql
index d7bb00d5e..6a53ed845 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowConflictException.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowConflictException.sql
@@ -1,5 +1,4 @@
-
-
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
CREATE PROCEDURE [dbo].[spThrowConflictException]
@Message NVARCHAR(2048) = NULL
@@ -7,6 +6,4 @@ AS
BEGIN
SET NOCOUNT ON;
THROW 56006, @Message, 1
-END
-
-
+END
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowDuplicateException.sql b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowDuplicateException.sql
index a9ca24202..2946623b9 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowDuplicateException.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowDuplicateException.sql
@@ -1,4 +1,6 @@
-CREATE PROCEDURE [dbo].[spThrowDuplicateException]
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
+
+CREATE PROCEDURE [dbo].[spThrowDuplicateException]
@Message NVARCHAR(2048) = NULL
AS
BEGIN
diff --git a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowNotFoundException.sql b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowNotFoundException.sql
index 73437d729..56eb2adf1 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowNotFoundException.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowNotFoundException.sql
@@ -1,7 +1,9 @@
-CREATE PROCEDURE [dbo].[spThrowNotFoundException]
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
+
+CREATE PROCEDURE [dbo].[spThrowNotFoundException]
@Message NVARCHAR(2048) = NULL
AS
BEGIN
SET NOCOUNT ON;
THROW 56005, @Message, 1
-END
+END
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowValidationException.sql b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowValidationException.sql
index 8630d5b09..70c2e8810 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowValidationException.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Stored Procedures/spThrowValidationException.sql
@@ -1,5 +1,4 @@
-
-
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
CREATE PROCEDURE [dbo].[spThrowValidationException]
@Message NVARCHAR(2048) = NULL
@@ -7,6 +6,4 @@ AS
BEGIN
SET NOCOUNT ON;
THROW 56001, @Message, 1
-END
-
-
+END
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtBigIntList.sql b/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtBigIntList.sql
index d2139d997..f7994b2be 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtBigIntList.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtBigIntList.sql
@@ -1,4 +1,6 @@
-CREATE TYPE [dbo].[udtBigIntList] AS TABLE
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
+
+CREATE TYPE [dbo].[udtBigIntList] AS TABLE
(
[Value] BIGINT
-)
+)
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtDateTime2List.sql b/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtDateTime2List.sql
index 5a7fc11d2..76e6c73a8 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtDateTime2List.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtDateTime2List.sql
@@ -1,4 +1,6 @@
-CREATE TYPE [dbo].[udtDateTime2List] AS TABLE
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
+
+CREATE TYPE [dbo].[udtDateTime2List] AS TABLE
(
[Value] DATETIME2
-)
+)
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtIntList.sql b/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtIntList.sql
index 764eddbf7..d18cd369e 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtIntList.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtIntList.sql
@@ -1,4 +1,6 @@
-CREATE TYPE [dbo].[udtIntList] AS TABLE
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
+
+CREATE TYPE [dbo].[udtIntList] AS TABLE
(
[Value] INT
-)
+)
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtNVarCharList.sql b/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtNVarCharList.sql
index 12dfe2eb8..9222567ac 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtNVarCharList.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtNVarCharList.sql
@@ -1,4 +1,6 @@
-CREATE TYPE [dbo].[udtNVarCharList] AS TABLE
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
+
+CREATE TYPE [dbo].[udtNVarCharList] AS TABLE
(
[Value] NVARCHAR(MAX)
-)
+)
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtUniqueIdentfierList.sql b/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtUniqueIdentfierList.sql
index 8dc71143f..018c41232 100644
--- a/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtUniqueIdentfierList.sql
+++ b/tools/Beef.Database.Core/Schema/dbo/Types/User-Defined Table Types/udtUniqueIdentfierList.sql
@@ -1,4 +1,6 @@
-CREATE TYPE [dbo].[udtUniqueIdentifierList] AS TABLE
+-- Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef
+
+CREATE TYPE [dbo].[udtUniqueIdentifierList] AS TABLE
(
[Value] UNIQUEIDENTIFIER
-)
+)
\ No newline at end of file
diff --git a/tools/Beef.Database.Core/Sql/SqlObjectReader.cs b/tools/Beef.Database.Core/Sql/SqlObjectReader.cs
index 287e6d6f5..768c93462 100644
--- a/tools/Beef.Database.Core/Sql/SqlObjectReader.cs
+++ b/tools/Beef.Database.Core/Sql/SqlObjectReader.cs
@@ -126,6 +126,9 @@ private void Parse()
continue;
// Remove comments.
+ if (txt.StartsWith("--", StringComparison.InvariantCulture))
+ continue;
+
var ci = txt.IndexOf("--", StringComparison.InvariantCulture);
if (ci >= 0)
txt = txt.Substring(0, ci - 1).TrimEnd();