Radarr/src/Marr.Data/Mapping/RelationshipBuilder.cs

173 lines
5.9 KiB
C#

using System;
using System.Linq;
using System.Linq.Expressions;
using Marr.Data.Mapping.Strategies;
namespace Marr.Data.Mapping
{
/// <summary>
/// This class has fluent methods that are used to easily configure relationship mappings.
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class RelationshipBuilder<TEntity>
{
private FluentMappings.MappingsFluentEntity<TEntity> _fluentEntity;
private string _currentPropertyName;
public RelationshipBuilder(FluentMappings.MappingsFluentEntity<TEntity> fluentEntity, RelationshipCollection relationships)
{
_fluentEntity = fluentEntity;
Relationships = relationships;
}
/// <summary>
/// Gets the list of relationship mappings that are being configured.
/// </summary>
public RelationshipCollection Relationships { get; private set; }
#region - Fluent Methods -
/// <summary>
/// Initializes the configurator to configure the given property.
/// </summary>
/// <param name="property"></param>
/// <returns></returns>
public RelationshipBuilder<TEntity> For(Expression<Func<TEntity, object>> property)
{
return For(property.GetMemberName());
}
/// <summary>
/// Initializes the configurator to configure the given property or field.
/// </summary>
/// <param name="propertyName"></param>
/// <returns></returns>
public RelationshipBuilder<TEntity> For(string propertyName)
{
_currentPropertyName = propertyName;
// Try to add the relationship if it doesn't exist
if (Relationships[_currentPropertyName] == null)
{
TryAddRelationshipForField(_currentPropertyName);
}
return this;
}
/// <summary>
/// Sets a property to be lazy loaded, with a given query.
/// </summary>
/// <typeparam name="TChild"></typeparam>
/// <param name="query"></param>
/// <param name="condition">condition in which a child could exist. eg. avoid call to db if foreign key is 0 or null</param>
/// <returns></returns>
public RelationshipBuilder<TEntity> LazyLoad<TChild>(Func<IDataMapper, TEntity, TChild> query, Func<TEntity, bool> condition = null)
{
AssertCurrentPropertyIsSet();
Relationships[_currentPropertyName].LazyLoaded = new LazyLoaded<TEntity, TChild>(query, condition);
return this;
}
public RelationshipBuilder<TEntity> SetOneToOne()
{
AssertCurrentPropertyIsSet();
SetOneToOne(_currentPropertyName);
return this;
}
public RelationshipBuilder<TEntity> SetOneToOne(string propertyName)
{
Relationships[propertyName].RelationshipInfo.RelationType = RelationshipTypes.One;
return this;
}
public RelationshipBuilder<TEntity> SetOneToMany()
{
AssertCurrentPropertyIsSet();
SetOneToMany(_currentPropertyName);
return this;
}
public RelationshipBuilder<TEntity> SetOneToMany(string propertyName)
{
Relationships[propertyName].RelationshipInfo.RelationType = RelationshipTypes.Many;
return this;
}
public RelationshipBuilder<TEntity> Ignore(Expression<Func<TEntity, object>> property)
{
string propertyName = property.GetMemberName();
Relationships.RemoveAll(r => r.Member.Name == propertyName);
return this;
}
public FluentMappings.MappingsFluentTables<TEntity> Tables
{
get
{
if (_fluentEntity == null)
{
throw new Exception("This property is not compatible with the obsolete 'MapBuilder' class.");
}
return _fluentEntity.Table;
}
}
public FluentMappings.MappingsFluentColumns<TEntity> Columns
{
get
{
if (_fluentEntity == null)
{
throw new Exception("This property is not compatible with the obsolete 'MapBuilder' class.");
}
return _fluentEntity.Columns;
}
}
public FluentMappings.MappingsFluentEntity<TNewEntity> Entity<TNewEntity>()
{
return new FluentMappings.MappingsFluentEntity<TNewEntity>(true);
}
/// <summary>
/// Tries to add a Relationship for the given field name.
/// Throws and exception if field cannot be found.
/// </summary>
private void TryAddRelationshipForField(string fieldName)
{
// Set strategy to filter for public or private fields
ConventionMapStrategy strategy = new ConventionMapStrategy(false);
// Find the field that matches the given field name
strategy.RelationshipPredicate = mi => mi.Name == fieldName;
Relationship relationship = strategy.MapRelationships(typeof(TEntity)).FirstOrDefault();
if (relationship == null)
{
throw new DataMappingException(string.Format("Could not find the field '{0}' in '{1}'.",
fieldName,
typeof(TEntity).Name));
}
Relationships.Add(relationship);
}
/// <summary>
/// Throws an exception if the "current" property has not been set.
/// </summary>
private void AssertCurrentPropertyIsSet()
{
if (string.IsNullOrEmpty(_currentPropertyName))
{
throw new DataMappingException("A property must first be specified using the 'For' method.");
}
}
#endregion
}
}