using System.Linq.Expressions;
namespace zero.Architecture;
///
///
///
public class Blueprint : Blueprint where T : ZeroEntity, new()
{
public List> UnlockedFields { get; private set; } = new();
///
/// These fields are not copied by the ObjectCloner
/// as they are either locked or copied manually by ApplyDefaults()
///
protected static string[] DefaultUncopyFields { get; set; } = new[] { "id", "url", "name", "alias", "key", "sort", "isActive", "hash", "languageId", "createdById", "createdDate", "lastModifiedById", "lastModifiedDate", "blueprint" };
public Blueprint() : base(typeof(T)) { }
///
/// Merges blueprint data into a model based on the configuration
///
public virtual T Apply(T blueprint, T model)
{
// reset blueprint options for model in case no blueprint was found
if (blueprint == null || model.Blueprint == null)
{
model.Blueprint = null;
return model;
}
// copy all properties which are synced from the blueprint to the model
ApplyDefaults(blueprint, model);
ObjectCopycat.CopyProperties(blueprint, model, DefaultUncopyFields.Union(model.Blueprint.Desync).ToArray());
return model;
}
///
public override ZeroEntity Apply(ZeroEntity blueprint, ZeroEntity model) => Apply(blueprint as T, model as T);
///
/// Update default entity data
///
protected virtual void ApplyDefaults(T blueprint, T model)
{
if (!model.Blueprint.Desync.Contains(nameof(ZeroEntity.Name), StringComparer))
{
model.Name = blueprint.Name;
model.Alias = blueprint.Alias;
}
if (!model.Blueprint.Desync.Contains(nameof(ZeroEntity.Key), StringComparer))
{
model.Key = blueprint.Key;
}
if (!model.Blueprint.Desync.Contains(nameof(ZeroEntity.Sort), StringComparer))
{
model.Sort = blueprint.Sort;
}
if (!model.Blueprint.Desync.Contains(nameof(ZeroEntity.IsActive), StringComparer))
{
model.IsActive = blueprint.IsActive;
}
// these values can't be overridden
model.Hash = blueprint.Hash;
model.LanguageId = blueprint.LanguageId;
model.CreatedById = blueprint.CreatedById;
model.CreatedDate = blueprint.CreatedDate;
model.LastModifiedById = blueprint.LastModifiedById;
model.LastModifiedDate = blueprint.LastModifiedDate;
}
///
/// Lock a field so it always get synchronized no and cannot be changed
///
public void LockAll()
{
UnlockedFields = new();
}
///
/// Lock a field so it always get synchronized no and cannot be changed
///
public void Lock(Expression> selector)
{
RemoveField(selector);
}
public void Unlock(Expression> selector)
{
AddField(selector);
}
public void UnlockDefaults()
{
AddField(x => x.Name);
AddField(x => x.Alias);
AddField(x => x.Sort);
AddField(x => x.Key);
AddField(x => x.IsActive);
}
///
public override IEnumerable GetUnlockedFieldNames()
{
foreach (BlueprintField field in UnlockedFields)
{
yield return field.FieldName.ToCamelCaseId();
}
}
///
/// Get existing field or create a new one
///
protected BlueprintField AddField(Expression> selector)
{
BlueprintField tempField = new(selector);
BlueprintField storedField = UnlockedFields.FirstOrDefault(x => x.FieldName.Equals(tempField.FieldName, StringComparison.InvariantCultureIgnoreCase));
if (storedField != null)
{
return storedField;
}
UnlockedFields.Add(tempField);
return tempField;
}
///
/// Removes a field
///
protected void RemoveField(Expression> selector)
{
BlueprintField tempField = new(selector);
if (tempField != null)
{
UnlockedFields.RemoveAll(x => x.FieldName.Equals(tempField.FieldName, StringComparison.InvariantCultureIgnoreCase));
}
}
}
///
///
///
public abstract class Blueprint
{
///
/// Type of the associated entity
///
public Type ContentType { get; private set; }
public string Alias { get; private set; }
///
/// String comparer for property name comparison
///
protected readonly StringComparer StringComparer = StringComparer.InvariantCultureIgnoreCase;
public Blueprint(Type type)
{
ContentType = type;
Alias = type.Name.ToCamelCaseId();
}
///
/// Merges blueprint data into a model based on the configuration
///
public abstract ZeroEntity Apply(ZeroEntity blueprint, ZeroEntity model);
///
/// Get all fields which have been unlocked
///
public abstract IEnumerable GetUnlockedFieldNames();
}
public class DefaultBlueprint : Blueprint where T : ZeroEntity, new()
{
public DefaultBlueprint(Action> expression = null) : base()
{
expression?.Invoke(this);
}
}