Entities File Settings

Settings for scaffolding out an entity for your Web API.

Entities

A list of database entities in your project. These entities are added as dbsets to your database context, so they translate 1:1 to tables in your database. With that said, if you want to create an aggregate, you can scaffold out an entity and update it accordingly to reflect that aggregate yourself (e.g. remove the db set).

Entity Properties

NameRequiredDescriptionDefault
NameYesThe name of the entityNone
PropertiesYesA list of properties assigned to your entity described in the entity properties section.None
FeaturesYesThis is a list of features that you want to add for a particular entity.None
PluralNoThe plural of the entity name, if needed (e.g. Cities would prevent Citys)Entity Name appended with an s
LambdaNoThe value to use in lambda expressions for this entity.First letter of the entity name.
TableNameNo (Yes if you want to set a Schema)The name of the table in the database.Defaulted to the singular Name.
SchemaNoThe schema to use in the database.None, which will equate to dbo in SqlServer, public in Postgres, or mydb in MySQL

TableName is required if you want to set a schema

Properties

A list of properties that can be assigned to an entity.

NameRequiredDescriptionDefault
NameYesThe name of the propertyNone
TypeNoThe data type for the property. These are not case sensitive and they can be set to nullable with a trailing ?. All standard C# data types can be used here. You even do custom properties like classes that act as foreign keys for instance.string
IsRequiredNoWhen true, the property will be set as required in the database.false
true for primary key
ColumnNameNoThe database field name for the given property.None
CanManipulateNoWhen set to false, you will not be able to update this property when calling the associated endpoint. When set to false, the property will be able to be established when using the POST endpoint, but will not be able to be updated after that. This is managed by the DTOs if you want to manually adjust this.true
false for primary key
DefaultValueNoAllows you to add a default value to a property. Note that it should be entered exactly as you'd want it, so a string would be something like "My Default Value", a bool might be true, and an int might be 0None
ForeignEntityNameNoCaptures the name of the entity this property is linked to as a foreign key.None
ForeignEntityPluralNoThe plural value for the foreign entity, if applicable.Entity Name appended with an s
IsChildRelationshipNoOnly used when establishing a relationship. Designates whether or not a relationship property is being set from the child entity.false
SmartNamesNoA list of strings used to create a smart enum property. Each string denotes an option for the smart enum. See the smart enum example below for details. Property attributes like filtering and sorting can be used if desired.None

Features

NameRequiredDescriptionDefault
TypeYesThe type of feature you want to add. The accepted values are AdHoc, Job, GetRecord, GetList, GetAll, DeleteRecord, UpdateRecord, AddRecord, and CreateRecord (same as AddRecord but available as an alias in case you can't remember!)None
IsProtectedNoDetermines whether or not the feature is protected with an authorization policy attribute.false
PermissionNameNoThe name of the authorization policy attribute.Can[FEATURENAME]

GetRecord, GetList, GetAll, DeleteRecord, UpdateRecord, AddRecord, and AddListByFk are all standard CRUD features along with their endpoints and associated tests.

ℹ DTOs, validators, profiles and functional test routes associated to all features will be added regardless of whether or not a given feature was added. This is due to the complexity of all the minute management around handling these so dynamically. Feel free to delete whatever you're not using.

AddListByFk Feature

The AddListByFk will let you add a list or batch of records using a foreign key. For example, you can add a list of ingredients for a recipe, based on the id of a recipe.

NameRequiredDescriptionDefault
BatchPropertyNameYesThe name of the property on the foreign entity you are doing a batch add on.None
BatchPropertyTypeYesThe data type of the the property you are doing the batch add on. This is for a FK property, so probably a Guid or int.None
ParentEntityYesThe name of the parent entity that the FK you using is associated to. For example, if you had a FK of EventId, the parent entity might be Event. Leave null if you're not batching on a FK.None
ParentEntityPluralYesThe plural of the FK entity. Leave null if you're not batching on a FK.None

Hangfire Jobs

You can also add features that will be setup for a Hangfire job instead of a MediatR handler using the Job feature type.

NameRequiredDescriptionDefault
NameYesThe name of the feature. This is the name of the feature's method and would generally be something like AddCustomer or GetCustomer.None
EntityPluralNoThe plural name of the entity that the feature will be added to (should match the name in the Domain directory). You can leave this blank to add it to the Domain directory directly.[ENTITYNAME]s

Ad Hoc Features

You can also add ad hoc skeletons with the AdHoc type. For AdHoc features, there's a bit more that needs to be done. In addition to Type above, you'll want to provide the below:

NameRequiredDescriptionDefault
NameYesThe name of the feature. This is the name of the feature's method and would generally be something like AddCustomer or GetCustomer.None
EntityPluralNoThe plural name of the entity that the feature will be added to (should match the name in the Domain directory). You can leave this blank to add it to the Domain directory directly.[ENTITYNAME]s
ResponseTypeNoIs the type of response that this command will return. This could be any C# type that you want, or even a custom type if you want, but in that case, you'll need to add a using statement for it manually.bool

You can also add any of the above features after the fact using the add feature command.

Keys and Relationships

There are a couple of important pieces of information to know about settings keys for scaffolding:

  1. As v0.12+, you do not need to add a primary key property to your entities as they will automatically be inherited from the BaseEntity.
  2. Foreign keys do not need to be added. See how relationships are managed below.

Relationships can be set up by adding a special property that looks something like this:

- Name: Product
  Relationship: manyto1
  ForeignEntityName: Product
  ForeignEntityPlural: Products

The different type of relationships are:

  • 1tomany where the principal entity is the 1 and the foreign entity is the many. For example, a Customer can have many Orders.
  • manyto1 where the principal entity is the many and the foreign entity is the 1. For example, a Product can have many Categories.
  • manytomany where the principal entity is the entity that the property is added to. For example, a Product can have many Tags.
  • 1to1 where the principal entity is the entity that the property is added to. For example, a Customer can have one CustomerSettings.
  • self where the principal entity is the same as the foreign entity. For example, a Category can have many SubCategories.

Note that child entities are managed from the principal entity. So for example, if you have a Customer that manage Order entities through something like Customer.AddOrder(), the Customer would be the principal and you would use a 1tomany on the Customer. If you wanted to manage the Customer from an Order entity with something like Order.SetCustomer(), you would use a manyto1 on the Order entity. The main question you should ask yourself when adding a relationship and picking which entity to put it on should be 'where should this concept be managed from?'. This will help you establish the principal owner of the relationship.

If you're doing an add entities command and your principal entity is already in the project, you can still add the relationship by adding an IsChildRelationship: true.

💡 Note that you'll need to flip the relationship type when adding a child relationship. So if you would normally add a 1tomany Order relationship on a Customer, when adding it as a child on Order, you'll want to make the Customer relationship a manyto1 relationship.

Notice the difference in the examples below, with the first being the principal based example and the second being the child based example.

- Name: Customer
  Features:
    - Type: GetList
    - Type: GetAll
    - Type: GetRecord
    - Type: AddRecord
    - Type: UpdateRecord
    - Type: DeleteRecord
    - Type: Job
      Name: PerformCustomerMigration
  Properties:
    - Name: FirstName
    - Name: LastName
    - Name: Email
    - Name: DateJoined
      Type: DateOnly?
    - Name: LoyaltyPoints
      Type: int?
    - Name: Orders
      Relationship: 1tomany
      ForeignEntityName: Order
      ForeignEntityPlural: Orders
- Name: Order
  Features:
    - Type: GetList
    - Type: GetRecord
    - Type: AddRecord
    - Type: UpdateRecord
    - Type: DeleteRecord
  Properties:
    - Name: OrderDate
      Type: DateOnly?
    - Name: Status
      CanManipulate: false
    - Name: Customer
      Relationship: manyto1
      IsChildRelationship: true
      ForeignEntityName: Customer
      ForeignEntityPlural: Customers

Entity Example

Here's a larger example that has all the different kinds of relationships.

DomainName: ECommerceApi
BoundedContexts:
  - ProjectName: ECommerce
    Port: 5300
    DbContext:
      ContextName: ECommerceDbContext
      DatabaseName: ECommerceDB
      Provider: Postgres
    Entities:
      - Name: Customer
        Features:
          - Type: GetList
          - Type: GetAll
          - Type: GetRecord
          - Type: AddRecord
          - Type: UpdateRecord
          - Type: DeleteRecord
          - Type: Job
            Name: PerformCustomerMigration
        Properties:
          - Name: FirstName
          - Name: LastName
          - Name: Email
          - Name: DateJoined
            Type: DateOnly?
          - Name: LoyaltyPoints
            Type: int?
          - Name: Orders
            Relationship: 1tomany
            ForeignEntityName: Order
            ForeignEntityPlural: Orders
          - Name: Settings
            Relationship: 1to1
            ForeignEntityName: CustomerSettings
            ForeignEntityPlural: CustomerSettings
      - Name: Product
        Features:
          - Type: GetList
          - Type: GetRecord
          - Type: AddRecord
          - Type: UpdateRecord
          - Type: DeleteRecord
        Properties:
          - Name: ProductName
          - Name: Description
          - Name: Price
            Type: decimal?
          - Name: Stock
            Type: int?
          - Name: Category
            Relationship: manyto1
            ForeignEntityName: Category
            ForeignEntityPlural: Categories
          - Name: Tags
            Relationship: manytomany
            ForeignEntityName: Tag
            ForeignEntityPlural: Tags
      - Name: Category
        Plural: Categories
        Features:
          - Type: GetList
          - Type: GetRecord
          - Type: AddRecord
          - Type: UpdateRecord
          - Type: DeleteRecord
        Properties:
          - Name: CategoryName
          - Name: ParentCategory
            Relationship: self
            ForeignEntityName: Category
            ForeignEntityPlural: Categories
      - Name: Order
        Features:
          - Type: GetList
          - Type: GetRecord
          - Type: AddRecord
          - Type: UpdateRecord
          - Type: DeleteRecord
        Properties:
          - Name: OrderDate
            Type: DateOnly?
          - Name: Status
            CanManipulate: false
          - Name: OrderItems
            Relationship: 1tomany
            ForeignEntityName: OrderItem
            ForeignEntityPlural: OrderItems
      - Name: OrderItem
        Features:
          - Type: GetList
          - Type: GetRecord
          - Type: AddRecord
          - Type: UpdateRecord
          - Type: DeleteRecord
        Properties:
          - Name: Quantity
            Type: int
          - Name: Product
            Relationship: manyto1
            ForeignEntityName: Product
            ForeignEntityPlural: Products
      - Name: Review
        Features:
          - Type: GetList
          - Type: GetRecord
          - Type: AddRecord
          - Type: UpdateRecord
          - Type: DeleteRecord
        Properties:
          - Name: Rating
            Type: int
          - Name: Comment
          - Name: Product
            Relationship: manyto1
            ForeignEntityName: Product
            ForeignEntityPlural: Products
          - Name: Customer
            Relationship: manyto1
            ForeignEntityName: Customer
            ForeignEntityPlural: Customers
      - Name: Tag
        Features:
          - Type: GetList
          - Type: GetRecord
          - Type: AddRecord
          - Type: UpdateRecord
          - Type: DeleteRecord
        Properties:
          - Name: Name
          - Name: Description
      - Name: CustomerSettings
        Plural: CustomerSettings
        Features:
          - Type: GetList
          - Type: GetRecord
          - Type: AddRecord
          - Type: UpdateRecord
          - Type: DeleteRecord
        Properties:
          - Name: PreferredDayOfArrival

Composite Keys

For the time being, composite keys can not be created using Craftsman commands. With that said, you are more than welcome to scaffold out an entity with one of the keys and then modify the generated entity, repository, tests, etc. to accommodate that composite key.

Policies

If using auth, you can protect features with a particular permission. In the example below, I have a permission for doing read overall and default permissions for each destructive feature.

Entities:
  - Name: Recipe
    Features:
      - Type: GetList
        IsProtected: true
        PermissionName: CanReadRecipes
      - Type: GetRecord
        IsProtected: true
        PermissionName: CanReadRecipes
      - Type: AddRecord
        IsProtected: true
      - Type: UpdateRecord
        IsProtected: true
      - Type: DeleteRecord
        IsProtected: true
    Properties:
      - Name: Title
        Type: string
      - Name: Directions
        Type: string

Smart Enums

If you want to use smart enums, you can use the SmartNames property like so:

Entities:
  - Name: Recipe
    Features:
    #...
    Properties:
      - Name: Title
        Type: string
      - Name: Visibility
        SmartNames:
          - Public
          - Friends Only
          - Private