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