Domain

Entities and O/R Mapping

Overview

The domain tier contains all entity classes that are mapped to database tables. Some of the most used classes are:

An entity class is a "Plain old CLR object" (POCO). It usually represents one database table, with each property typically representing one column in the table.

You can establish any relationship between entities (1:1, 1:n, n:m)

BaseEntity

A concrete entity class must derive from the abstract class BaseEntity. By convention:

  • BaseEntity.Id is used as the primary key.

  • All public properties with a getter and a setter will be included in the database schema.

To customize the default mapping conventions:

  • Use the DataAnnotation attributes on classes and properties.

  • Write Fluent API mapping code.

Refer to Entity Framework Core: Creating and configuring a model to learn more about data modelling in Entity Framework.

For performance reasons, Smartstore does not use proxy classes for lazy loading, but the ILazyLoader interface instead. Therefore, a protected LazyLoader property that allows lazy loading of navigation properties is declared in the BaseEntity class. You don't need to inject the ILazyLoader service, the property is automatically injected when an entity is attached to the context. If an entity is loaded without being tracked, or if it is manually detached from the context, the NullLazyLoader - which does nothing - will be injected instead.

Lazy loader usage example:

public class MyEntity : BaseEntity 
{
    // 1:1 navigation property
    private Product _product;
    public Product Product
    {
        // The "LazyLoader" property is declared in the "BaseEntity" class
        get => _product ?? LazyLoader?.Load(this, ref _product);
        set => _product = value;
    }
    
    // 1:n navigation property
    private ICollection<Category> _categories;
    public ICollection<Category> Categories
    {
        // The "LazyLoader" property is declared in "BaseEntity" class
        get => LazyLoader?.Load(this, ref _categories) ?? (_categories ??= new HashSet<Category>());
        // "protected" --> prevent assignment
        protected set => _categories = value;
    }
}

Domain assemblies

On app start-up Smartstore scans all application core assemblies for entity types. If you created a new entity class and want to make it accessible, there are different approaches depending on the project type.

In a core project simply extend the partial SmartDbContext class.

public partial class SmartDbContext
{
    public DbSet<MyEntity> MyEntities { get; set; }
}

In a module project you need to inform Entity Framework that your module assembly contains entity types to pickup and register on start-up.

For this you need a starter class.

internal class Startup : StarterBase
{
    public override void ConfigureServices(IServiceCollection services, IApplicationContext appContext)
    {
        services.AddTransient<IDbContextConfigurationSource<SmartDbContext>, SmartDbContextConfigurer>();
    }

    class SmartDbContextConfigurer : IDbContextConfigurationSource<SmartDbContext>
    {
        public void Configure(IServiceProvider services, DbContextOptionsBuilder builder)
        {
            builder.UseDbFactory(b =>
            {
                b.AddModelAssembly(this.GetType().Assembly);
            });
        }
    }
}

For more comfort while developing you can add this extension:

Extensions/SmartDbContextExtensions.cs
public static class SmartDbContextExtensions
{
    public static DbSet<MyEntity> MyEntities(this SmartDbContext db)
        => db.Set<MyEntity>();
}

Entity characteristics

Numerous interfaces can be implemented and some abstract base classes can be derived from to specify what an entity has/is, supports or represents.

The following types declare some properties your entity must implement in order to follow the contract.

Interfaces

Name
Characteristic

IAclRestricted

Has access restrictions

IAttributeAware

Has some raw attributes

IAuditable

Has auditing properties

IDiscountable

Has applicable discounts

IDisplayedEntity

Is displayed somehow in UI

IDisplayOrder

Is orderable

ILocalizedEntity

Is localizable

INamedEntity

Has a conceptual name

IMergedData

Has mergeable data

IPagingOptions

Has data paging options (page size etc.)

IRulesContainer

Is a container for other rules

ISlugSupported

Supports SEO slugs

ISoftDeletable

Is soft deletable

IStoreRestricted

Supports store mapping

ITransient

Supports transiency

Abstract base classes

Name
Characteristic

EntityWithAttributes

Has generic attributes (see Generic attributes)

EntityWithDiscounts

Has applicable discounts

CustomerContent

Represents content entered by customer

Last updated

Was this helpful?