From a9f1e7ccf01623fe60d6427d3876e4e1e9c41638 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Wed, 20 Mar 2024 16:05:17 -0700 Subject: [PATCH] docs: improvements for beginners to reduce confusion --- docs/.vitepress/config.mts | 9 ++- docs/modeling/model-types/entities.md | 61 ------------------- .../model-types/standalone-entities.md | 61 +++++++++++++++++++ .../components/c-input.md | 2 +- .../components/c-select.md | 2 +- docs/stacks/vue/getting-started.md | 17 +++++- 6 files changed, 86 insertions(+), 66 deletions(-) create mode 100644 docs/modeling/model-types/standalone-entities.md diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 03941ce28..e99c0b5e3 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -85,8 +85,15 @@ export default defineConfig({ items: [ autoTitle("/modeling/model-types/entities"), autoTitle("/modeling/model-types/external-types"), - autoTitle("/modeling/model-types/dtos"), autoTitle("/modeling/model-types/services"), + { + text: "Advanced", + collapsed: true, + items: [ + autoTitle("/modeling/model-types/dtos"), + autoTitle("/modeling/model-types/standalone-entities"), + ] + }, ], }, { diff --git a/docs/modeling/model-types/entities.md b/docs/modeling/model-types/entities.md index cbb694189..0c7f2ea01 100644 --- a/docs/modeling/model-types/entities.md +++ b/docs/modeling/model-types/entities.md @@ -39,64 +39,3 @@ The method by which you can control what data the users of your application can Behaviors in Coalesce are to mutating data as data sources are to reading data. Defining a behaviors class for a model allows complete control over the way that Coalesce will create, update, and delete your application's data in response to requests made through its generated API. Read [Behaviors](/modeling/model-components/behaviors.md) to learn more. - -## Standalone (non-EF) Entities - -In Coalesce, Standalone Entities are entity types that are not based on Entity Framework. These types are discovered by Coalesce by annotating them with `[Coalesce, StandaloneEntity]`. - -For these types, you must define at least one custom [Data Source](/modeling/model-components/data-sources.md), and optionally a [Behaviors](/modeling/model-components/behaviors.md) class as well. If no behaviors are defined, the type is implicitly read-only, equivalent to turning off create/edit/delete via the [Security Attributes](/modeling/model-components/attributes/security-attribute.md). - -To define data sources and behaviors for Standalone Entities, it is recommended you inherit from `StandardDataSource` and `StandardBehaviors`, respectively. For example: - -``` c# -[Coalesce, StandaloneEntity] -public class StandaloneExample -{ - public int Id { get; set; } - - [Search(SearchMethod = SearchAttribute.SearchMethods.Contains), ListText] - public string Name { get; set; } = ""; - - [DefaultOrderBy] - public DateTimeOffset Date { get; set; } - - private static int nextId = 0; - private static ConcurrentDictionary backingStore = new ConcurrentDictionary(); - - public class DefaultSource : StandardDataSource - { - public DefaultSource(CrudContext context) : base(context) { } - - public override Task> GetQueryAsync(IDataSourceParameters parameters) - => Task.FromResult(backingStore.Values.AsQueryable()); - } - - public class Behaviors : StandardBehaviors - { - public Behaviors(CrudContext context) : base(context) { } - - public override Task ExecuteDeleteAsync(StandaloneExample item) - { - backingStore.TryRemove(item.Id, out _); - return Task.CompletedTask; - } - - public override Task ExecuteSaveAsync(SaveKind kind, StandaloneExample? oldItem, StandaloneExample item) - { - if (kind == SaveKind.Create) - { - item.Id = Interlocked.Increment(ref nextId); - backingStore.TryAdd(item.Id, item); - } - else - { - backingStore.TryRemove(item.Id, out _); - backingStore.TryAdd(item.Id, item); - } - return Task.CompletedTask; - } - } -} -``` - -The above example is admittedly contrived, as it is unlikely that you would be using an in-memory collection as a data persistence mechanism. A more likely real-world scenario would be to inject an interface to some other data store. Data Source and Behavior classes are instantiated using your application's service provider, so any registered service can be injected. \ No newline at end of file diff --git a/docs/modeling/model-types/standalone-entities.md b/docs/modeling/model-types/standalone-entities.md new file mode 100644 index 000000000..6e8082a34 --- /dev/null +++ b/docs/modeling/model-types/standalone-entities.md @@ -0,0 +1,61 @@ + +# Standalone Entities + +In Coalesce, Standalone Entities are types that behave like [entity types](./entities.md) (they can support the full suite generated CRUD endpoints), but are not based on Entity Framework. These types are discovered by Coalesce by annotating them with `[Coalesce, StandaloneEntity]`. + +For these types, you must define at least one custom [Data Source](/modeling/model-components/data-sources.md), and optionally a [Behaviors](/modeling/model-components/behaviors.md) class as well. If no behaviors are defined, the type is implicitly read-only, equivalent to turning off create/edit/delete via the [Security Attributes](/modeling/model-components/attributes/security-attribute.md). + +To define data sources and behaviors for Standalone Entities, it is recommended you inherit from `StandardDataSource` and `StandardBehaviors`, respectively. For example: + +``` c# +[Coalesce, StandaloneEntity] +public class StandaloneExample +{ + public int Id { get; set; } + + [Search(SearchMethod = SearchAttribute.SearchMethods.Contains), ListText] + public string Name { get; set; } = ""; + + [DefaultOrderBy] + public DateTimeOffset Date { get; set; } + + private static int nextId = 0; + private static ConcurrentDictionary backingStore = new ConcurrentDictionary(); + + public class DefaultSource : StandardDataSource + { + public DefaultSource(CrudContext context) : base(context) { } + + public override Task> GetQueryAsync(IDataSourceParameters parameters) + => Task.FromResult(backingStore.Values.AsQueryable()); + } + + public class Behaviors : StandardBehaviors + { + public Behaviors(CrudContext context) : base(context) { } + + public override Task ExecuteDeleteAsync(StandaloneExample item) + { + backingStore.TryRemove(item.Id, out _); + return Task.CompletedTask; + } + + public override Task ExecuteSaveAsync(SaveKind kind, StandaloneExample? oldItem, StandaloneExample item) + { + if (kind == SaveKind.Create) + { + item.Id = Interlocked.Increment(ref nextId); + backingStore.TryAdd(item.Id, item); + } + else + { + backingStore.TryRemove(item.Id, out _); + backingStore.TryAdd(item.Id, item); + } + return Task.CompletedTask; + } + } +} +``` + +The above example is admittedly contrived, as it is unlikely that you would be using an in-memory collection as a data persistence mechanism. A more likely real-world scenario would be to inject an interface to some other data store. Data Source and Behavior classes are instantiated using your application's service provider, so any registered service can be injected. \ No newline at end of file diff --git a/docs/stacks/vue/coalesce-vue-vuetify/components/c-input.md b/docs/stacks/vue/coalesce-vue-vuetify/components/c-input.md index f1d52fced..627766487 100644 --- a/docs/stacks/vue/coalesce-vue-vuetify/components/c-input.md +++ b/docs/stacks/vue/coalesce-vue-vuetify/components/c-input.md @@ -2,7 +2,7 @@ -A general-purpose input component for most [Values](/stacks/vue/layers/metadata.md). c-input does not have much functionality of its own - instead, it delegates to the right kind of component based on the type of value to which it is bound. This includes both other [Coalesce Vuetify Components](/stacks/vue/coalesce-vue-vuetify/overview.md) as well as direct usages of some [Vuetify](https://vuetifyjs.com/) components. +A general-purpose input component for most [Values](/stacks/vue/layers/metadata.md). c-input delegates to other components based on the type of value it is bound to. This includes both other [Coalesce Vuetify Components](/stacks/vue/coalesce-vue-vuetify/overview.md) as well as direct usages of some [Vuetify](https://vuetifyjs.com/) components. diff --git a/docs/stacks/vue/coalesce-vue-vuetify/components/c-select.md b/docs/stacks/vue/coalesce-vue-vuetify/components/c-select.md index c33793098..b657d14ed 100644 --- a/docs/stacks/vue/coalesce-vue-vuetify/components/c-select.md +++ b/docs/stacks/vue/coalesce-vue-vuetify/components/c-select.md @@ -4,7 +4,7 @@ A dropdown component that allows for selecting values fetched from the generated ``/list`` API endpoints. -Used both for selecting values for foreign key and navigation properties, and for selecting arbitrary objects or primary keys independent of a parent or owning object. +Used for selecting values for foreign key and navigation properties, or for selecting arbitrary objects or primary keys without a parent or owning object. diff --git a/docs/stacks/vue/getting-started.md b/docs/stacks/vue/getting-started.md index 680405b18..81938269e 100644 --- a/docs/stacks/vue/getting-started.md +++ b/docs/stacks/vue/getting-started.md @@ -1,6 +1,19 @@ # Getting Started +## Environment Setup +Before you begin, ensure that you have all the requisite tools installed +- Recent version of the [.NET SDK](https://dotnet.microsoft.com/en-us/download). If you have Visual Studio, you already have this. +- A recent version of [Node.js](https://nodejs.org/) (an LTS version is recommended). +- A compatible IDE. + - Recommended: + - Visual Studio for backend (C#) development + - VS Code for frontend (Vue, TypeScript) development (with [Vue - Official](https://marketplace.visualstudio.com/items?itemName=Vue.volar) and [TypeScript Vue Plugin](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin)) + - Also consider: + - VS Code for full stack development. + - JetBrains Rider + + ## Creating a Project The quickest and easiest way to create a new Coalesce Vue application is to use the ``dotnet new`` template. In your favorite shell: @@ -26,9 +39,9 @@ The Vue template is based on [Vite](https://vitejs.dev/). You are strongly encou The structure of the Web project follows the conventions of both ASP.NET Core and Vite. The Vue-specific folders are as follows: -- ``/src`` - Files that should be compiled into your application. CSS/SCSS, TypeScript, Vue SFCs, and so on. +- ``/src`` - Files that should be compiled into your frontend application. CSS/SCSS, TypeScript, Vue SFCs, and so on. - ``/public`` - Static assets that should be served as files. Includes index.html, the root document of the application. -- ``/wwwroot`` - Target for compiled output. +- ``/wwwroot`` - Target for compiled output. This directory is excluded from git. During development, no special tooling is required to build your frontend code. Coalesce's ``UseViteDevelopmentServer`` in ASP.NET Core will take care of that automatically when the application starts. Just make sure NPM packages have been installed (`npm ci`).