Designing Entities in the LemonEdge Platform
This article explains how to create the following simple entities in the system from code:
- Country: A simple entity holding the following properties:
- ISO Code
- Description
- Policy: A simple entity holding references to existing entities, and a country to show how they are all the same, along with a property with validation
- Name
- Description
- CurrencyID
- CountryID
- Price
Important
The most important point about entities created through our designers, or in code here, is that they are exactly the same as any real entity within the system itself. In other words all the entities in the LemonEdge platform are created in the same way using our own API. The entities you create are no different. They are not user data, configuration, or any other "hack" that means they suffer performance, reporting, auditing, etc or other penalities. These entities are created for real in the database, and contain all the same functionality of the system entities in the platform.
Tip
This article assumes you are familiar with the following LemonEdge concepts:
- Creating An AddIn: How to create a .net dll referencing the LemonEdge dlls.
- The LemonEdge API Attributes: These are all the attributes that will mark the entities you will be creating in ways the LemonEdge system can understand.
Note
You can follow along with this article by downloading the API Examples projects which provide the code examples discussed here.
Entity Concepts
Before we create an entity, it is important to understand come core concepts around how an entity works within the LemonEdge platform. This can be broken down into the following areas:
- Unique Entity IDs
- Interface Definitions
Unique Entity IDs
Within the LemonEdge platform each individual entity type must be able to be uniquely identified. Each entity type therefore has a unique Guid associated with it. This is used to refer to the entity type throughout the system such as in permissions, and other areas. This way to uniquely identify any item in the system you need the Entity Type ID, and the ID of the individual item itself.
Tip
This also provides a mechanism to ensure that created entity types are the same throughout multiple databases. For instance if you have a web service loading your "Country" entity type from database A and database B, by them both having the same entity type id, the service knows they are the same type and not to duplicate classes or other functionality.
All the entities in the system are uniquely identified using the EntityID enum for convenience. This enum value is actually translated to a unique Guid by the ToShortGuid() extension method ensuring all entities have a unique type id, while providing a simple mechanism to refer to the standard system entities.
When creating an entity you will need to generate your own unique Guid. You can do that using any method:
- CSharp:
Guid.NewGuid()
- SQL:
select NEWID()
- Online: Click here for generator
- etc...
You can then refer to that entity type throughout the system using your unique Guid.
Tip
We recommend hard coding them into a global class so you can refer to them easily throughout your code like so:
public static class GlobalEntityID
{
public const string MyEntityType = "004d3f29-4157-497d-bfef-c8df1ec630bd";
public static Guid MyEntityTypeID => Guid.Parse(MyEntityType);
}
Interface Definitions
When defining entities in the LemonEdge system they are all defined as interfaces with attributes against them. This is so interface definitions can be worked against by 3rd parties while keeping your internal functionality proprietary, among other reasons. Throughout the entire LemonEdge platform you do not need to refer to the classes themselves, and can query, update and work with everything through the interface definitions alone.
The base interfaces, that all entities must inherit from, include functionality required of all LemonEdge items such as Cloning Implementations, Property Change Tracking, and standard component model INotifyPropertyChanged, and INotifyPropertyChanging implementations.
If you want to implement the classes for these interfaces you must implement all the interface functionality. You can do that via the following methods:
- Create the class definitions for your interfaces entirely yourselves, implementing all the functionality.
- Inherit our BaseEntity class which implements all the base interfaces, and add your additional interface functionality whilst utilising our base implementations where required.
- Use our AutoCode Generator tool against your interface to have the system automatically generate the class implementation for you that inherits our base classes and fills out everything needed with partial method implementations for your optional implementations where appropriate.
Tip
The simplest option is to not bother creating a class implementation at all. The system will dynamically create the class for internal use, and you can refer to the items and use them fully throughout the entire LemonEdge platform simply through the interface definition alone.
Country Entity
To create a country entity we first need to assign it a unique entity type id. We can do that like so:
namespace MyApp
{
public static class GlobalEntityTypes
{
public const string Country = "004d3f29-4157-497d-bfef-c8df1ec630bd";
public static Guid CountryID => Guid.Parse(Country);
}
}
Warning
Obviously for your own implementation you would create your own unique Guid for your entities. Do not re-use this one.
Next we simply create the interface definition which describes the entity we would like in the system:
namespace MyApp.Interfaces
{
public interface ICountry : LemonEdge.API.Core.IBaseEntity
{
string ISOCode { get; set; }
string Description { get; set; }
}
}
The important point is that any entity in the system must inherit ultimately from IBaseEntity in order for it to work correctly within the LemonEdge platform.
Note
The neat part about this approach, is you would have to design this interface no matter what system you would work with, and especially if you were to start creating your own solution from scratch. Thus any algorithms you create that work with ICountry, or any of your other entities, would still be an algorithm you could use on other platforms against that same interface. Ultimately wherever possible we're trying to make sure you have ownership over your code and the ability to re-use it wherever you need to.
Next we simply need to mark this interface with LemonEdge attributes that describe this interface as an entity the system can understand:
namespace MyApp.Interfaces
{
[LemonEdge.API.Attributes.EntityDefinition(MyApp.GlobalEntityTypes.Country, "dbo.CE_Countries", "Country", IsStandingDataEntity = true, LabelColumn = nameof(Description), SetName = "Countries")]
public interface ICountry : LemonEdge.API.Core.IBaseEntity
{
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.NVarChar, 500, false)]
[LemonEdge.API.Attributes.EntityKeyProperty]
[System.ComponentModel.DataAnnotations.Required]
string ISOCode { get; set; }
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.NVarChar, 2500, false)]
[System.ComponentModel.DataAnnotations.Required]
string Description { get; set; }
}
}
Tip
You can also add the Default Entity Icon attribute to your entity to specify the image that should be used by default whenever viewing or opening this type of entity. See Using Custom Images for an example and more information.
Breaking this down by each attribute, we can go through them all in more detail
Entity Definition Attribute
[LemonEdge.API.Attributes.EntityDefinition(MyApp.GlobalEntityTypes.Country, "dbo.CE_Countries", "Country", IsStandingDataEntity = true, LabelColumn = "Description", SetName = "Countries")]
public interface ICountry : LemonEdge.API.Core.IBaseEntity
This is the core EntityDefinition attribute that marks an interface (that must inherit IBaseEntity) as being an entity for use within the LemonEdge platform. All entities in LemonEdge must be marked with this attribute which defines the following key properties for integration with the platform:
TypeID: The unique global hardcoded ID for this entity type.
In this example we're using 'MyApp.GlobalEntityTypes.Country'
TableName: Holds the name of the actual table in the database that holds all records for this type.
All system LemonEdge entities have tables that always start with 'LT_'.
In this example we're using 'dbo.CE_Countries'
ItemName: The singular name the system should use when referring to an instance of this entity.
This should contain alpha numerical characters only, not spaces or other special characters
In this example we're using 'Country'
IsStandingDataEntity: Indicates this entity is part of the standing data/configuation for the system and that it should be loaded on application startup as part of the cache.
Do not use for tables of large data sets.
In this example we're using 'true'
LabelColumn: If this entity is displayed as an entity in the system that can be opened, viewed, or referenced by the user anywhere then this holds the property to be used as a label.
Whenever a property is a relationship the system also creates a '_Label' column for the property id that holds the label of that related item using this property. For instance an entity holding a CountryID property pointing to this country entity, the system will also automatically create a CountryID_Label property to hold the value of the Description field of the Country for the label value.
In this example we're using 'Description' as the property that holds the label the user should see whenever viewing a country.
SetName: The name the system should use when referring to a collection of these entities. This should contain alpha numerical characters only, not spaces or other special characters
If this property is ommitted the system automatically provides a value using the Pluralize extension.
In this example we're using 'Countries'.
See the EntityDefinition attribute documentation for a complete list of properties for against an entity type, such as index configuration. help files, and more.
Entity Property Attribute
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.NVarChar, 500, false)]
string ISOCode { get; set; }
This is the core EntityProperty attribute that details to LemonEdge how the property should be created in the sql server database. ALl properties on the interface that will exist in the database as columns must be marked with this attribute. Using this attribute ensures the system can construct a ColumnType for the property which defines how to create it in sql. This attribute defines the following key properties for the ISOCode property itself:
Type: The sql type used to store this property value in sql server. The enum SQLType holds a direct mapping for all the sql server types.
In this example we're using the nvarchar sql type.
Length: The max length for this nvarchar column in sql. If null is supplied then nvarchar(max) would be used in sql.
In this example we're saying the maximum length for the ISOCode is 500 characters
Nullable: Indicates if the column should be nullable in sql. If so this should be a nullable property in .net too if the type is a value type.
In this exazmple we're using 'false' to indicate the ISOCode can not be null
See the EntityProperty attribute documentation for a complete list of all the options, such as sparse, nullable, friendly labels and more, you can set against a property on your entity
Important
In order to correctly create a ColumnType, the system has the following mandatory properties depending on the sql type:
- Text: If the column is text type in sql, such as nvarchar, varchar, text, or similar, then you also need to provide a Length value. This specified the max length for the text field, or if null indicates it defaults to nvarchar(max) or similar.
- Decimal: If the column type is a sql decimal then you also need to provide a precision and scale which will translate to the decimal(precision, scale) in sql
Warning
Dates and datetimes should be stored as a DateTimeOffset as per most standard practices instead of the old datetime sql format.
This also ensures compatability with odata as datetime is not supported - see here. OData is currently used by LemonEdge when connecting via the web service.
If you are using a datetime to store only a date, and you do not care about the time component then you can use the IsDate property of the attribute to ensure the system universally only uses the date component of the property.
Tip
You can also provide a default sql value for the property. This is useful as the system automatically upgrades entities in the database as you change their schema. If you add a property to an entity, the system will automatically add this column to the relevant tables in sql server during an upgrade. If you've made this a non-nullable column then you can provide a default value for the column using this property. For instance, adding the following property would ensure all existing records would have a default of 0:
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.Int, false, "0")]
int MyNewProperty Value { get; set; }
Entity Key Property Attribute
[LemonEdge.API.Attributes.EntityKeyProperty]
string ISOCode { get; set; }
This attribute indicates that this property should form part of the key property for uniquely identifying a record of this type.
All entities in the system hold an internal unique Guid ID property that they inherit from IBaseEntity. This key is more what property, or properties, uniquely identify you custom entity. These are used by the system to uniquely identify records during import/exporting, or to enforce validation, among other functions.
In this example for our Country entity we are saying that the ISOCode uniquely identifies a country record. When importing the system knows to update a record if a country with the specified ISO Code already exists. If we marked another property with the EntityKeyProperty attribute as well then that combination would uniquely identify the records, instead of a single property.
Component Model
[System.ComponentModel.DataAnnotations.Required]
string ISOCode { get; set; }
Any standard ComponetModel validation attributes can be added as normal and their validation is enforced throughout the system during the SaveChanges process.
Property Attributes
The Description property for our Country entity, then uses the same above attributes to mark the property as being a nvarchar of length 2500 that is a required property.
There are a host of other attributes for marking against your properties, see the API.Attributes and API.Attributes.Validation namespace documentation for more information.
Policy Entity
To create a policy entity we can follow the same steps as our country entity, it is that simple:
namespace MyApp
{
public static class GlobalEntityTypes
{
public const string Country = "004d3f29-4157-497d-bfef-c8df1ec630bd";
public static Guid CountryID => Guid.Parse(Country);
public const string Policy = "1820d6cd-ca55-4934-8ed3-92178ee7c673";
public static Guid PolicyID => Guid.Parse(Policy);
}
}
Warning
Obviously for your own implementation you would create your own unique Guid for your entities. Do not re-use these.
Next we simply create the interface definition which describes the entity we would like in the system:
namespace MyApp.Interfaces
{
[LemonEdge.API.Attributes.EntityDefinition(MyApp.GlobalEntityTypes.Policy, "dbo.CE_Policies", "Policy", IsStandingDataEntity = false, LabelColumn = nameof(Name), SetName = "Policies")]
public interface IPolicy : LemonEdge.API.Core.IBaseEntityWithPermissions
{
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.NVarChar, 500, false)]
[LemonEdge.API.Attributes.EntityKeyProperty]
[System.ComponentModel.DataAnnotations.Required]
string Name { get; set; }
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.NVarChar, 2500, false)]
[System.ComponentModel.DataAnnotations.Required]
string Description { get; set; }
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.UniqueIdentifier, false)]
[LemonEdge.API.Attributes.Validation.RequiredNoDefaultIDValidation]
[LemonEdge.API.Attributes.EntityRelationship(LemonEdge.API.Attributes.EntityID.Currency, "ID", LemonEdge.Utils.Database.SingleJoinType.One, "Currency", "Policies")]
Guid CurrencyID { get; set; }
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.UniqueIdentifier, false)]
[LemonEdge.API.Attributes.Validation.RequiredNoDefaultIDValidation]
[LemonEdge.API.Attributes.EntityRelationship(MyApp.GlobalEntityTypes.Country, "ID", LemonEdge.Utils.Database.SingleJoinType.One, "Country", "Policies")]
Guid CountryID { get; set; }
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.Decimal, 12, 3, false)]
[System.ComponentModel.DataAnnotations.Required]
[LemonEdge.API.Attributes.Validation.PropertyValidation("true", "MyItem.Price > 1000", "Price must be > 1k")]
decimal Price { get; set; }
}
}
The first difference here is that we are inheriting from IBaseEntityWithPermissions which simply indicates to the system that this entity can have individual permissions set against it. Typically an entity will either have its own set of permissions, or inherit permissions from a related entity. In this case we are stating policies can be permissioned by the platform, and can be configured through Teams - see here for more information.
Tip
If instead we wanted them to inherit permissions from a relationship, say curreny, then we would set the InheritPermissions property of the EntityRelationship attribute to true.
Policies also introduce a few new attributes as follows:
Required No Default ID Validation Attribute
[LemonEdge.API.Attributes.Validation.RequiredNoDefaultIDValidation]
Guid CurrencyID { get; set; }
This indicates the property is required and must not have the default value for this structure. In this case the empty value "00000000-0000-0000-0000-000000000000" is not a valid value. See RequiredNoDefaultIDValidation for more information along with all other attributes in LemonEdge.API.Attributes.Validation.
Entity Relationship Attribute
[LemonEdge.API.Attributes.EntityRelationship(LemonEdge.API.Attributes.EntityID.Currency, "ID", LemonEdge.Utils.Database.SingleJoinType.One, "Currency", "Policies")]
Guid CurrencyID { get; set; }
This attribute is the main attribute used to inform the platform this property holds a relationship to another entity. The system uses this to perform the following core actions, among others:
- Enforce the integrity of the relationship
- Inform the platform the properties involved in the relationship to maintain:
- Automatically ensure the relationships are all available in the reporting tools
- Automatically ensure labels are available throughout the UI for the linked to entity in the relationship
- Perform permission inheritance, delete cascading and other options set via the attribute itself
Important
The entity in the relationship is indicated by the global unique Guid for the entity the relationship points to. In this case we are pointing to a system entity and thus can use the system entity enum for ease: LemonEdge.API.Attributes.EntityID.Currency.
If the relationship points to a custom entity, then you need the global unique Guid for that entity. In the case of the Country relationship that is indicated with our MyApp.GlobalEntityTypes.Country value.
Property Validation Attribute
[LemonEdge.API.Attributes.Validation.PropertyValidation("true", "MyItem.Price > 1000", "Price must be > 1k")]
decimal Price { get; set; }
This attribute allows us to add custom validation rules against the property whenever a record is being saved. In this example we are saying that a record is not valid, and will not be saved, if the Price is <= 1000.
Tip
MyItem is a property on the Formula Functions With Context class that is used to evaluate formulas in this property. MyItem holds the instance of the Policy entity being validated.
The formula engine evaluates c# script, and for this attribute evaluates the result to true or false for passing a validation.
See PropertyValidation for more information along with all other attributes in LemonEdge.API.Attributes.Validation
Complete Design
Putting all of the above together, our very simple Custom DLL to create Countries and Policies would look like the following:
namespace MyApp
{
public static class GlobalEntityTypes
{
public const string Country = "004d3f29-4157-497d-bfef-c8df1ec630bd";
public static Guid CountryID => Guid.Parse(Country);
public const string Policy = "1820d6cd-ca55-4934-8ed3-92178ee7c673";
public static Guid PolicyID => Guid.Parse(Policy);
}
namespace Interfaces
{
[LemonEdge.API.Attributes.EntityDefinition(MyApp.GlobalEntityTypes.Country, "dbo.CE_Countries", "Country", IsStandingDataEntity = true, LabelColumn = nameof(Description), SetName = "Countries")]
public interface ICountry : LemonEdge.API.Core.IBaseEntity
{
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.NVarChar, 500, false)]
[LemonEdge.API.Attributes.EntityKeyProperty]
[System.ComponentModel.DataAnnotations.Required]
string ISOCode { get; set; }
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.NVarChar, 2500, false)]
[System.ComponentModel.DataAnnotations.Required]
string Description { get; set; }
}
[LemonEdge.API.Attributes.EntityDefinition(MyApp.GlobalEntityTypes.Policy, "dbo.CE_Policies", "Policy", IsStandingDataEntity = false, LabelColumn = nameof(Name), SetName = "Policies")]
public interface IPolicy : LemonEdge.API.Core.IBaseEntityWithPermissions
{
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.NVarChar, 500, false)]
[LemonEdge.API.Attributes.EntityKeyProperty]
[System.ComponentModel.DataAnnotations.Required]
string Name { get; set; }
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.NVarChar, 2500, false)]
[System.ComponentModel.DataAnnotations.Required]
string Description { get; set; }
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.UniqueIdentifier, false)]
[LemonEdge.API.Attributes.Validation.RequiredNoDefaultIDValidation]
[LemonEdge.API.Attributes.EntityRelationship(LemonEdge.API.Attributes.EntityID.Currency, "ID", LemonEdge.Utils.Database.SingleJoinType.One, "Currency", "Policies")]
Guid CurrencyID { get; set; }
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.UniqueIdentifier, false)]
[LemonEdge.API.Attributes.Validation.RequiredNoDefaultIDValidation]
[LemonEdge.API.Attributes.EntityRelationship(MyApp.GlobalEntityTypes.Country, "ID", LemonEdge.Utils.Database.SingleJoinType.One, "Country", "Policies")]
Guid CountryID { get; set; }
[LemonEdge.API.Attributes.EntityProperty(LemonEdge.Utils.Database.SQLType.Decimal, 12, 3, false)]
[System.ComponentModel.DataAnnotations.Required]
[LemonEdge.API.Attributes.Validation.PropertyValidation("true", "MyItem.Price > 1000", "Price must be > 1k")]
decimal Price { get; set; }
}
}
}
That's it.
In less than 50 lines of code you've created country and policy entities with the following functionality:
- Tables created/upgraded/etc in the database automatically for you
- Including auditing, permissions, and other tables where appropriate
- All the functions created to support querying the entities
- All the stored procedures to support CRUD operations
- Upgraded the web service to support querying and updating countries and policies
- Integration with all LemonEdge Platform features:
- Auditing
- Querying/Reporting
- Permissions
- Canvas Technology
- Multi-Tenant Support
- Server Task Support
- The core IEntityRetriever context supports querying all your entities regardless of LemonEdge connection type
- The core IEntityUpdater context supports updating all your entities regardless of LemonEdge connection type
- Integration with LemonEdge UI supporting multi-platform client applications in Windows, Linux, Web Browser and more.
Auto-Code Designers
You can also use our Auto-Code Designers to build everything you've just done in the product itself without writing a single line of code. The designers allow you to create both those entities and dynamically load them into the platform as required.
Tip
The key difference with other configuration tools is that our designers are not a "black-box". you'll never hit a wall in what you can achieve as if you can't get anywhere else with the designers they can actually generate the code for you. The designers aren't configuring the system, they literally dynamically write the code for what you configure and dynamically load them into the platform as required. Thus you can always export the code, and tweak it to the nth degree to achieve 100% of your requirements. You can then take that code and load it back into the system as an actual AddIn.
For example, we have an Auto-Code Designer walkthrough tutorial that builds the same entities as we've just coded manually here. You can step through that walthrough here. If you export the auto-code designer code for the policy it will automatically generate the following for you:
/*
Auto Generated file by LemonEdge © 2020
Warning: Any changes to this file will be overwritten if it is regenerated.
*/
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Text;
using LemonEdge.Entities;
using LemonEdge.API.Core;
using LemonEdge.API.Entities;
using System.Linq;
using System.Threading.Tasks;
using System.Reflection;
using LemonEdge.Core;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Runtime.Serialization;
using LemonEdge.API.Attributes;
namespace LemonEdge.Entities
{
[EntityDefinition("f73ce6bb-3f2e-401a-bc00-ac690129b216", "dbo.CE_Policies", "Policy", IsStandingDataEntity = false, LabelColumn = "Name", SetName = "Policies")]
public interface IPolicy :
IBaseEntity
{
[EntityProperty(LemonEdge.Utils.Database.SQLType.NVarChar, 500, false, defaultSQL: "", ColumnName = "Name")]
[EntityKeyProperty]
[System.ComponentModel.DataAnnotations.Required]
String Name { get; set; }
[EntityProperty(LemonEdge.Utils.Database.SQLType.NVarChar, 2500, false, defaultSQL: "", ColumnName = "Description")]
[System.ComponentModel.DataAnnotations.Required]
String Description { get; set; }
[EntityProperty(LemonEdge.Utils.Database.SQLType.UniqueIdentifier, false, defaultSQL: "", ColumnName = "CurrencyID")]
[LemonEdge.API.Attributes.Validation.RequiredNoDefaultIDValidation]
[EntityRelationship("00000000-0000-0000-0000-00000000000f", "ID", LemonEdge.Utils.Database.SingleJoinType.One, "CurrencyID", "Policies", InheritPermissions = false, DeleteWithRelationship = false, PartOfParentSet = false, LinkToItemInSet = false, PreventAutoConstraint = false)]
Guid CurrencyID { get; set; }
[EntityProperty(LemonEdge.Utils.Database.SQLType.UniqueIdentifier, false, defaultSQL: "", ColumnName = "CountryID")]
[LemonEdge.API.Attributes.Validation.RequiredNoDefaultIDValidation]
[EntityRelationship("a08fc929-47c1-499b-9900-ac6901295bdf", "ID", LemonEdge.Utils.Database.SingleJoinType.One, "CountryID", "Policies", InheritPermissions = false, DeleteWithRelationship = false, PartOfParentSet = false, LinkToItemInSet = false, PreventAutoConstraint = false)]
Guid CountryID { get; set; }
[EntityProperty(LemonEdge.Utils.Database.SQLType.Decimal, 12, 3, false, defaultSQL: "", ColumnName = "Price")]
[System.ComponentModel.DataAnnotations.Required]
[LemonEdge.API.Attributes.Validation.PropertyValidation("true", "MyItem.Price > 1000", "Price must be > 1k")]
Decimal Price { get; set; }
}
[DataContract]
public partial class Policy :
LemonEdge.API.Core.BaseEntity,
IPolicy,
LemonEdge.Utils.Interfaces.ICloneableAsync<IPolicy>
{
[DataMember]
private String _name;
[DataMember]
private String _description;
[DataMember]
private Guid _currencyID;
[DataMember]
private string _currencyID_Label;
[DataMember]
private Guid _countryID;
[DataMember]
private string _countryID_Label;
[DataMember]
private Decimal _price;
[System.ComponentModel.DataAnnotations.Required]
[System.ComponentModel.DataAnnotations.StringLength(500)]
public String Name
{
get => _name;
set
{
if(_name != value)
{
OnPropertyChanging(nameof(Name));
_name = value;
OnPropertyChanged(nameof(Name));
OnNameChanged();
}
}
}
partial void OnNameChanged();
[System.ComponentModel.DataAnnotations.Required]
[System.ComponentModel.DataAnnotations.StringLength(2500)]
public String Description
{
get => _description;
set
{
if(_description != value)
{
OnPropertyChanging(nameof(Description));
_description = value;
OnPropertyChanged(nameof(Description));
OnDescriptionChanged();
}
}
}
partial void OnDescriptionChanged();
[LemonEdge.API.Attributes.Validation.RequiredNoDefaultIDValidation]
public Guid CurrencyID
{
get => _currencyID;
set
{
if(_currencyID != value)
{
OnPropertyChanging(nameof(CurrencyID));
_currencyID = value;
OnPropertyChanged(nameof(CurrencyID));
OnCurrencyIDChanged();
}
}
}
partial void OnCurrencyIDChanged();
public String CurrencyID_Label
{
get => _currencyID_Label;
set
{
if(_currencyID_Label != value)
{
OnPropertyChanging(nameof(CurrencyID_Label));
_currencyID_Label = value;
OnPropertyChanged(nameof(CurrencyID_Label));
OnCurrencyID_LabelChanged();
}
}
}
partial void OnCurrencyID_LabelChanged();
[LemonEdge.API.Attributes.Validation.RequiredNoDefaultIDValidation]
public Guid CountryID
{
get => _countryID;
set
{
if(_countryID != value)
{
OnPropertyChanging(nameof(CountryID));
_countryID = value;
OnPropertyChanged(nameof(CountryID));
OnCountryIDChanged();
}
}
}
partial void OnCountryIDChanged();
public String CountryID_Label
{
get => _countryID_Label;
set
{
if(_countryID_Label != value)
{
OnPropertyChanging(nameof(CountryID_Label));
_countryID_Label = value;
OnPropertyChanged(nameof(CountryID_Label));
OnCountryID_LabelChanged();
}
}
}
partial void OnCountryID_LabelChanged();
[System.ComponentModel.DataAnnotations.Required]
[LemonEdge.API.Attributes.Validation.PropertyValidation("true", "MyItem.Price > 1000", "Price must be > 1k")]
public Decimal Price
{
get => _price;
set
{
if(_price != value)
{
OnPropertyChanging(nameof(Price));
_price = value;
OnPropertyChanged(nameof(Price));
OnPriceChanged();
}
}
}
partial void OnPriceChanged();
#region ICloneableAsync Implementation
public void CopyFromSource(IPolicy source) => CopyFromEntity(source);
async Task<IPolicy> LemonEdge.Utils.Interfaces.ICloneableAsync<IPolicy>.Clone(object context) => (IPolicy) await Clone(context);
protected override void CopyFromEntity(IBaseEntity src)
{
base.CopyFromEntity(src);
var source = (IPolicy)src;
_name = source.Name;
_description = source.Description;
_currencyID = source.CurrencyID;
if(source is Policy s1) _currencyID_Label = s1.CurrencyID_Label;
_countryID = source.CountryID;
if(source is Policy s2) _countryID_Label = s2.CountryID_Label;
_price = source.Price;
OnCloning(src);
}
partial void OnCloning(IBaseEntity src);
#endregion
public override string ToString() => Name;
}
}
Note
The designer also dynamically generates the class implementations for you, which you can use or not. Typically if there's custom functionality you want to add to the class it makes sense to do so, otherwise you can ignore the class implementation and the platform will automatically generate it for internal use anyway.
Next Steps
Now you've created your entity code, or retrieved it from the auto-code designers dynamically generating it for you, you can create your dll and load it into the LemonEdge platform. It will then upgrade your database and be ready for immediate use.
See our Creating An AddIn In The LemonEdge Platform article for more information.
Once the entities are loaded you need to create a user interface in LemonEdge to view countries, and policies, and interact with them. See our Designing UIs article for more information.