diff --git a/.gitignore b/.gitignore
index 256b66e..9128333 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+
*.user
*.obj
*.lib
@@ -5,7 +6,6 @@
*.ncb
*.cache
*.gem
-[Ll]ib
[Oo]bj
[Bb]in
[Bb]uild
@@ -14,4 +14,7 @@
_ReSharper*/
*.vs10x
Source/Gallio Reports
-Source/TestResults
\ No newline at end of file
+Source/TestResults
+Source/_UpgradeReport_Files/
+*.orig
+Source/UpgradeLog*
\ No newline at end of file
diff --git a/Source/Debug.xunit b/Source/Debug.xunit
new file mode 100644
index 0000000..f6a167c
--- /dev/null
+++ b/Source/Debug.xunit
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/FluentMetadata.AutoMapper.Specs/FluentMetadata.AutoMapper.Specs.csproj b/Source/FluentMetadata.AutoMapper.Specs/FluentMetadata.AutoMapper.Specs.csproj
new file mode 100644
index 0000000..22f824b
--- /dev/null
+++ b/Source/FluentMetadata.AutoMapper.Specs/FluentMetadata.AutoMapper.Specs.csproj
@@ -0,0 +1,75 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {36BC8904-D8F8-4778-A5BB-F27F1346BAE9}
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ Library
+ Properties
+ FluentMetadata.AutoMapper.Specs
+ FluentMetadata.AutoMapper.Specs
+ v4.0
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\AutoMapper.1.1.0.118\lib\AutoMapper.dll
+
+
+
+
+
+
+
+
+ False
+ ..\..\external\xUnit\xunit.dll
+
+
+
+
+
+
+
+
+
+ {3EA6A0AC-8377-4C2F-A0EA-61278245620E}
+ FluentMetadata.AutoMapper
+
+
+ {C73F37FA-D859-4714-8335-35069322176A}
+ FluentMetadata.Core
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/FluentMetadata.AutoMapper.Specs/Properties/AssemblyInfo.cs b/Source/FluentMetadata.AutoMapper.Specs/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..3a174f7
--- /dev/null
+++ b/Source/FluentMetadata.AutoMapper.Specs/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("FluentMetadata.AutoMapper.Specs")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("FluentMetadata.AutoMapper.Specs")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("10e83014-1379-41a6-821d-50d5a1317676")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Source/FluentMetadata.AutoMapper.Specs/SystemUnderTest.cs b/Source/FluentMetadata.AutoMapper.Specs/SystemUnderTest.cs
new file mode 100644
index 0000000..2a5ebe5
--- /dev/null
+++ b/Source/FluentMetadata.AutoMapper.Specs/SystemUnderTest.cs
@@ -0,0 +1,64 @@
+using AutoMapper;
+namespace FluentMetadata.AutoMapper.Specs
+{
+ class Source
+ {
+ public string MyProperty { get; set; }
+ public int Named { get; set; }
+ public Nested Nested { get; set; }
+ public string StringField { get; set; }
+ }
+
+ class Nested
+ {
+ public FurtherNested FurtherNested { get; set; }
+ }
+
+ class FurtherNested
+ {
+ public int Id { get; set; }
+ }
+
+ class Destination
+ {
+ public string MyProperty { get; set; }
+ public int Renamed { get; set; }
+ public int NestedFurtherNestedId { get; set; }
+ public int IntProperty { get; set; }
+ }
+
+ class FakeResolver : ValueResolver
+ {
+ protected override int ResolveCore(string source)
+ {
+ return default(int);
+ }
+ }
+
+ class SourceMetaData : ClassMetadata
+ {
+ public SourceMetaData()
+ {
+ Class.Display.Name("rzjsfghgafsdfh");
+ Property(x => x.MyProperty).Display.Name("pockänsdfsdf");
+ Property(x => x.Named).Description("adföoiulkanhsda");
+ Property(x => x.StringField).UIHint("üoicvnqwnb");
+ }
+ }
+
+ class FurtherNestedMetaData : ClassMetadata
+ {
+ public FurtherNestedMetaData()
+ {
+ Property(x => x.Id).Is.Required();
+ }
+ }
+
+ class TargetMetaData : ClassMetadata
+ {
+ public TargetMetaData()
+ {
+ this.CopyAutoMappedMetadataFrom(typeof(Source));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/FluentMetadata.AutoMapper.Specs/When_copying_metadata_from_an_AutoMapped_Type.cs b/Source/FluentMetadata.AutoMapper.Specs/When_copying_metadata_from_an_AutoMapped_Type.cs
new file mode 100644
index 0000000..101fba2
--- /dev/null
+++ b/Source/FluentMetadata.AutoMapper.Specs/When_copying_metadata_from_an_AutoMapped_Type.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Linq;
+using AutoMapper;
+using Xunit;
+
+namespace FluentMetadata.AutoMapper.Specs
+{
+ public class When_copying_metadata_from_an_AutoMapped_Type : IDisposable
+ {
+ Metadata destinationMetadata,
+ destinationMyPropertyMetadata,
+ destinationRenamedMetadata,
+ destinationNestedFurtherNestedIdMetadata,
+ destinationIntPropertyMetadata;
+
+ public When_copying_metadata_from_an_AutoMapped_Type()
+ {
+ Mapper.CreateMap()
+ .ForMember(d => d.Renamed, o => o.MapFrom(s => s.Named))
+ .ForMember(d => d.IntProperty, o => o.ResolveUsing().FromMember(s => s.StringField));
+ Mapper.AssertConfigurationIsValid();
+
+ FluentMetadataBuilder.ForAssemblyOfType();
+
+ var query = new QueryFluentMetadata();
+ destinationMetadata = query.GetMetadataFor(typeof(Destination));
+ destinationMyPropertyMetadata = destinationMetadata.Properties
+ .Single(m => m.ModelName == "MyProperty");
+ destinationRenamedMetadata = destinationMetadata.Properties
+ .Single(m => m.ModelName == "Renamed");
+ destinationNestedFurtherNestedIdMetadata = destinationMetadata.Properties
+ .Single(m => m.ModelName == "NestedFurtherNestedId");
+ destinationIntPropertyMetadata = destinationMetadata.Properties
+ .Single(m => m.ModelName == "IntProperty");
+ }
+
+ [Fact]
+ public void a_destination_property_should_have_metadata_from_the_source_property_it_is_mapped_to()
+ {
+ Assert.Equal("pockänsdfsdf", destinationMyPropertyMetadata.GetDisplayName());
+ }
+
+ [Fact]
+ public void the_destination_type_should_have_metadata_from_the_source_type_it_is_mapped_to()
+ {
+ Assert.Equal("rzjsfghgafsdfh", destinationMetadata.GetDisplayName());
+ }
+
+ //TODO [on AutoMapper update] check if AutoMapper makes projected source property accessible
+ [Fact(Skip = "unsupported until AutoMapper makes projected source property accessible")]
+ public void a_projected_destination_property_should_have_metadata_from_the_source_property_it_is_mapped_to()
+ {
+ Assert.Equal("adföoiulkanhsda", destinationRenamedMetadata.Description);
+ }
+
+ [Fact]
+ public void a_flattened_destination_property_should_have_metadata_from_the_source_property_it_is_mapped_to()
+ {
+ Assert.Equal(true, destinationNestedFurtherNestedIdMetadata.Required);
+ }
+
+ //TODO [on AutoMapper update] check if AutoMapper makes source property that destination property is resolved from accessible
+ [Fact(Skip = "unsupported until AutoMapper makes the source property the destination property is resolved from accessible")]
+ public void a_destination_property_resolved_from_a_source_property_should_have_metadata_from_the_source_property()
+ {
+ Assert.Equal("üoicvnqwnb", destinationIntPropertyMetadata.TemplateHint);
+ }
+
+ public void Dispose()
+ {
+ FluentMetadataBuilder.Reset();
+ Mapper.Reset();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/FluentMetadata.AutoMapper.Specs/packages.config b/Source/FluentMetadata.AutoMapper.Specs/packages.config
new file mode 100644
index 0000000..8395173
--- /dev/null
+++ b/Source/FluentMetadata.AutoMapper.Specs/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Source/FluentMetadata.AutoMapper/AutoMapperHelper.cs b/Source/FluentMetadata.AutoMapper/AutoMapperHelper.cs
new file mode 100644
index 0000000..e5b0594
--- /dev/null
+++ b/Source/FluentMetadata.AutoMapper/AutoMapperHelper.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using AutoMapper;
+
+namespace FluentMetadata.AutoMapper
+{
+ internal class AutoMapperHelper
+ {
+ ///
+ /// Gets the mapped members for a source/destination type pair.
+ ///
+ /// The source Type.
+ /// The destination Type.
+ ///
+ internal static IEnumerable GetMemberMapsOf(Type source, Type destination)
+ {
+ var maps = new Collection();
+
+ foreach (var propertyMap in GetRelevantMappedMembersOf(source, destination))
+ {
+ // get the source property of the PropertyMap
+ var sourceValueResolvers = propertyMap.GetSourceValueResolvers()
+ // just plain property maps, there's no interesting metadata on a custom mapping function
+ .OfType();
+ var sourceProperty = sourceValueResolvers
+ // "There can be only one"
+ .SingleOrDefault(svr => svr.MemberType == propertyMap.DestinationProperty.MemberType);
+
+ if (sourceProperty != null)
+ {
+ maps.Add(new MemberMap
+ {
+ SourceName = sourceValueResolvers.Aggregate(string.Empty, (result, svr) => result + svr.Name),
+ DestinationName = propertyMap.DestinationProperty.Name
+ });
+ }
+ }
+
+ return maps;
+ }
+
+ ///
+ /// Gets the mapped members for a source/destination type pair
+ /// leaving out mapped members that are irrelevant.
+ ///
+ /// The source type.
+ /// The destination type.
+ ///
+ static IEnumerable GetRelevantMappedMembersOf(Type source, Type destination)
+ {
+ var typeMap = Mapper.FindTypeMapFor(source, destination);
+ return
+ typeMap != null ?
+ // filter by non-ignored PropertyMaps
+ typeMap.GetPropertyMaps().Where(m => m.IsIgnored() == false) :
+ Enumerable.Empty();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/FluentMetadata.AutoMapper/ClassMetadataAutoMapperExtensions.cs b/Source/FluentMetadata.AutoMapper/ClassMetadataAutoMapperExtensions.cs
new file mode 100644
index 0000000..81d6231
--- /dev/null
+++ b/Source/FluentMetadata.AutoMapper/ClassMetadataAutoMapperExtensions.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace FluentMetadata.AutoMapper
+{
+ ///
+ /// AutoMapper extensions to
+ ///
+ public static class ClassMetadataAutoMapperExtensions
+ {
+ ///
+ /// Copies the the source type's metadata to the destination type's metadata
+ /// using the mapping information provided by AutoMapper.
+ ///
+ /// The type of the destination.
+ /// The destination metadata.
+ /// The source type.
+ public static void CopyAutoMappedMetadataFrom(this ClassMetadata to, Type from)
+ {
+ var destinationType = typeof(TDestination);
+ MetadataHelper.CopyMappedMetadata(
+ from,
+ destinationType,
+ AutoMapperHelper.GetMemberMapsOf(from, destinationType)
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/FluentMetadata.AutoMapper/FluentMetadata.AutoMapper.csproj b/Source/FluentMetadata.AutoMapper/FluentMetadata.AutoMapper.csproj
new file mode 100644
index 0000000..da3d943
--- /dev/null
+++ b/Source/FluentMetadata.AutoMapper/FluentMetadata.AutoMapper.csproj
@@ -0,0 +1,65 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {3EA6A0AC-8377-4C2F-A0EA-61278245620E}
+ Library
+ Properties
+ FluentMetadata.AutoMapper
+ FluentMetadata.AutoMapper
+ v4.0
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\AutoMapper.1.1.0.118\lib\AutoMapper.dll
+
+
+
+
+
+
+ Properties\GlobalAssemblyInfo.cs
+
+
+
+
+
+
+
+
+
+
+ {C73F37FA-D859-4714-8335-35069322176A}
+ FluentMetadata.Core
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/FluentMetadata.AutoMapper/Properties/AssemblyInfo.cs b/Source/FluentMetadata.AutoMapper/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..5b6a4b9
--- /dev/null
+++ b/Source/FluentMetadata.AutoMapper/Properties/AssemblyInfo.cs
@@ -0,0 +1,13 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("FluentMetadata.AutoMapper")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("8e80ca63-7b53-49c9-b5e5-a708c8f3de7e")]
\ No newline at end of file
diff --git a/Source/FluentMetadata.AutoMapper/packages.config b/Source/FluentMetadata.AutoMapper/packages.config
new file mode 100644
index 0000000..8395173
--- /dev/null
+++ b/Source/FluentMetadata.AutoMapper/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core.Specs/Builder/DisplayBuilderTests.cs b/Source/FluentMetadata.Core.Specs/Builder/DisplayBuilderTests.cs
index 65dc3cf..ea3abee 100644
--- a/Source/FluentMetadata.Core.Specs/Builder/DisplayBuilderTests.cs
+++ b/Source/FluentMetadata.Core.Specs/Builder/DisplayBuilderTests.cs
@@ -29,7 +29,7 @@ public void DisplayBuilder_Ctor_Format_IsNull()
[Fact]
public void DisplayBuilder_Ctor_Name_IsNull()
{
- Assert.Null(metadata.DisplayName);
+ Assert.Null(metadata.GetDisplayName());
}
[Fact]
@@ -37,7 +37,7 @@ public void DisplayBuilder_NullText_NullText_IsValue()
{
builder.NullText("TheNullText");
Assert.Equal("TheNullText", metadata.NullDisplayText);
- Assert.Null(metadata.DisplayName);
+ Assert.Null(metadata.GetDisplayName());
Assert.Null(metadata.DisplayFormat);
}
@@ -45,7 +45,17 @@ public void DisplayBuilder_NullText_NullText_IsValue()
public void DisplayBuilder_Name_Name_IsValue()
{
builder.Name("TheNameText");
- Assert.Equal("TheNameText", metadata.DisplayName);
+ Assert.Equal("TheNameText", metadata.GetDisplayName());
+ Assert.Null(metadata.NullDisplayText);
+ Assert.Null(metadata.DisplayFormat);
+ }
+
+ [Fact]
+ public void DisplayBuilder_Name_Function_Equals_Metadata_DisplayName()
+ {
+ const string displayName = "asdf";
+ builder.Name(() => displayName);
+ Assert.Equal(displayName, metadata.GetDisplayName());
Assert.Null(metadata.NullDisplayText);
Assert.Null(metadata.DisplayFormat);
}
@@ -56,7 +66,7 @@ public void DisplayBuilder_Format_Format_IsValue()
builder.Format("TheFormatText");
Assert.Equal("TheFormatText", metadata.DisplayFormat);
Assert.Null(metadata.NullDisplayText);
- Assert.Null(metadata.DisplayName);
+ Assert.Null(metadata.GetDisplayName());
}
[Fact]
@@ -66,9 +76,8 @@ public void DisplayBuilder_Allset_IsValue()
builder.Name("TheNameText");
builder.NullText("TheNullText");
Assert.Equal("TheFormatText", metadata.DisplayFormat);
- Assert.Equal("TheNameText", metadata.DisplayName);
+ Assert.Equal("TheNameText", metadata.GetDisplayName());
Assert.Equal("TheNullText", metadata.NullDisplayText);
}
-
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core.Specs/ClassMetadata_with_Person.cs b/Source/FluentMetadata.Core.Specs/ClassMetadata_with_Person.cs
index 5725708..0410bbe 100644
--- a/Source/FluentMetadata.Core.Specs/ClassMetadata_with_Person.cs
+++ b/Source/FluentMetadata.Core.Specs/ClassMetadata_with_Person.cs
@@ -1,3 +1,5 @@
+using System.Linq;
+using FluentMetadata.Rules;
using FluentMetadata.Specs.SampleClasses;
using Xunit;
@@ -10,26 +12,41 @@ public class ClassMetadata_with_Person : MetadataTestBase
public ClassMetadata_with_Person()
{
var query = new QueryFluentMetadata();
- classMetadata = query.GetMetadataFor(typeof (Person));
+ classMetadata = query.GetMetadataFor(typeof(Person));
}
[Fact]
public void Metadata_ModelType_is_Person()
{
- Assert.Equal(typeof (Person), classMetadata.ModelType);
+ Assert.Equal(typeof(Person), classMetadata.ModelType);
}
[Fact]
public void Metadata_ModelName_is_Null()
{
- Assert.Null(classMetadata.ModelName);
-// Assert.Equal("Person", classMetadata.ModelName);
+ Assert.Null(classMetadata.ModelName);
}
[Fact]
public void Metadata_Display_is_Benutzer()
{
- Assert.Equal("Benutzer", classMetadata.DisplayName);
+ Assert.Equal("Benutzer", classMetadata.GetDisplayName());
+ }
+
+ [Fact]
+ public void Instance_with_FirstName_different_from_LastName_is_invalid()
+ {
+ var rule = classMetadata.Rules.OfType>().Last();
+ var person = new Person { FirstName = "foo", LastName = "bar" };
+ Assert.False(rule.IsValid(person));
+ }
+
+ [Fact]
+ public void Instance_with_FirstName_equal_to_LastName_is_valid()
+ {
+ var rule = classMetadata.Rules.OfType>().Last();
+ var person = new Person { FirstName = "foo", LastName = "foo" };
+ Assert.True(rule.IsValid(person));
}
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core.Specs/ClassMetadata_with_WebUser.cs b/Source/FluentMetadata.Core.Specs/ClassMetadata_with_WebUser.cs
index dba101d..367f30e 100644
--- a/Source/FluentMetadata.Core.Specs/ClassMetadata_with_WebUser.cs
+++ b/Source/FluentMetadata.Core.Specs/ClassMetadata_with_WebUser.cs
@@ -1,3 +1,6 @@
+using System;
+using System.Linq;
+using FluentMetadata.Rules;
using FluentMetadata.Specs.SampleClasses;
using Xunit;
@@ -28,8 +31,36 @@ public void ModeType_is_WebUser()
[Fact]
public void DisplayName_is_Benutzer()
{
- Assert.Equal("Benutzer", classMetadata.DisplayName);
+ Assert.Equal("Benutzer", classMetadata.GetDisplayName());
}
+ [Fact]
+ public void Generic_name_rule_is_valid_when_Username_is_not_equal_to_AutorName()
+ {
+ var nameRule = classMetadata.Rules
+ .OfType>()
+ .Single();
+
+ var webUser = new WebUser();
+ webUser.Username = "Holger";
+ webUser.Autor = new Autor { Name = "Albert" };
+
+ Assert.True(nameRule.IsValid(webUser));
+ }
+
+ [Fact]
+ public void Generic_name_rule_is_invalid_when_Username_is_equal_to_AutorName()
+ {
+ var nameRule = classMetadata.Rules
+ .OfType>()
+ .Single();
+
+ var webUser = new WebUser();
+ webUser.Username = "Holger";
+ webUser.Autor = new Autor { Name = "Holger" };
+
+ Console.WriteLine(nameRule.FormatErrorMessage(classMetadata.GetDisplayName()));
+ Assert.False(nameRule.IsValid(webUser));
+ }
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core.Specs/ClassMetadata_with_WebUserIndexModel.cs b/Source/FluentMetadata.Core.Specs/ClassMetadata_with_WebUserIndexModel.cs
index 7f888b2..aa2d717 100644
--- a/Source/FluentMetadata.Core.Specs/ClassMetadata_with_WebUserIndexModel.cs
+++ b/Source/FluentMetadata.Core.Specs/ClassMetadata_with_WebUserIndexModel.cs
@@ -17,7 +17,6 @@ public ClassMetadata_with_WebUserIndexModel()
public void ModelName_is_Null()
{
Assert.Null(classMetadata.ModelName);
-// Assert.Equal("WebUserIndexModel", classMetadata.ModelName);
}
[Fact]
@@ -29,7 +28,7 @@ public void ModeType_is_WebUserIndexModel()
[Fact]
public void DisplayName_is_Benutzer()
{
- Assert.Equal("Benutzer",classMetadata.DisplayName);
+ Assert.Equal("Benutzer", classMetadata.GetDisplayName());
}
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core.Specs/FluentMetadata.Core.Specs.csproj b/Source/FluentMetadata.Core.Specs/FluentMetadata.Core.Specs.csproj
index e8a0055..8745799 100644
--- a/Source/FluentMetadata.Core.Specs/FluentMetadata.Core.Specs.csproj
+++ b/Source/FluentMetadata.Core.Specs/FluentMetadata.Core.Specs.csproj
@@ -13,7 +13,8 @@
FluentMetadata.Core.Specs
v4.0
512
- 0
+
+
4.0
publish\
@@ -87,9 +88,12 @@
+
+
+
diff --git a/Source/FluentMetadata.Core.Specs/PropertyMedata_with_Person.cs b/Source/FluentMetadata.Core.Specs/PropertyMedata_with_Person.cs
index fb90c62..58569eb 100644
--- a/Source/FluentMetadata.Core.Specs/PropertyMedata_with_Person.cs
+++ b/Source/FluentMetadata.Core.Specs/PropertyMedata_with_Person.cs
@@ -11,8 +11,8 @@ public class PropertyMedata_with_Person : MetadataTestBase
public PropertyMedata_with_Person()
{
var query = new QueryFluentMetadata();
- firstName = query.GetMetadataFor(typeof (Person), "FirstName");
- lastName = query.GetMetadataFor(typeof (Person), "LastName");
+ firstName = query.GetMetadataFor(typeof(Person), "FirstName");
+ lastName = query.GetMetadataFor(typeof(Person), "LastName");
}
[Fact]
@@ -24,7 +24,7 @@ public void FirstName_ModelName_is_FirstName()
[Fact]
public void FirstName_ModelType_is_string()
{
- Assert.Equal(typeof (string), firstName.ModelType);
+ Assert.Equal(typeof(string), firstName.ModelType);
}
[Fact]
@@ -42,7 +42,7 @@ public void LastName_ModelName_is_LastName()
[Fact]
public void LastName_ModelType_is_string()
{
- Assert.Equal(typeof (string), lastName.ModelType);
+ Assert.Equal(typeof(string), lastName.ModelType);
}
[Fact]
diff --git a/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUser.cs b/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUser.cs
index 2dea710..5a948e0 100644
--- a/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUser.cs
+++ b/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUser.cs
@@ -1,18 +1,24 @@
-using FluentMetadata.Specs.SampleClasses;
+using System;
+using System.Linq;
+using FluentMetadata.Rules;
+using FluentMetadata.Specs.SampleClasses;
using Xunit;
namespace FluentMetadata.Specs
{
public class PropertyMedata_with_WebUser : MetadataTestBase
{
- private Metadata username;
- private Metadata id;
+ Metadata lastLogin, username, id, passWordHash, role, bounceCount;
public PropertyMedata_with_WebUser()
{
var query = new QueryFluentMetadata();
username = query.GetMetadataFor(typeof(WebUser), "Username");
id = query.GetMetadataFor(typeof(WebUser), "Id");
+ lastLogin = query.GetMetadataFor(typeof(WebUser), "LastLogin");
+ passWordHash = query.GetMetadataFor(typeof(WebUser), "PasswordHash");
+ role = query.GetMetadataFor(typeof(WebUser), "Role");
+ bounceCount = query.GetMetadataFor(typeof(WebUser), "BounceCount");
}
[Fact]
@@ -30,7 +36,7 @@ public void Username_ModelType_is_string()
[Fact]
public void Username_DisplayName_is_Benutzername()
{
- Assert.Equal("Benutzername", username.DisplayName);
+ Assert.Equal("Benutzername", username.GetDisplayName());
}
[Fact]
@@ -39,6 +45,42 @@ public void Username_Required_is_true()
Assert.True(username.Required.Value);
}
+ [Fact]
+ public void Username_MinLength_is_3()
+ {
+ Assert.Equal(3, username.GetMinimumLength());
+ }
+
+ [Fact]
+ public void Username_MaxLength_is_256()
+ {
+ Assert.Equal(256, username.GetMaximumLength());
+ }
+
+ [Fact]
+ public void PassWordHash_MinLength_is_32()
+ {
+ Assert.Equal(32, passWordHash.GetMinimumLength());
+ }
+
+ [Fact]
+ public void PassWordHash_MaxLength_is_null()
+ {
+ Assert.Null(passWordHash.GetMaximumLength());
+ }
+
+ [Fact]
+ public void Role_MinLength_is_null()
+ {
+ Assert.Null(role.GetMinimumLength());
+ }
+
+ [Fact]
+ public void Role_MaxLength_is_256()
+ {
+ Assert.Equal(256, role.GetMaximumLength());
+ }
+
[Fact]
public void Id_ModelName_is_Id()
{
@@ -56,5 +98,47 @@ public void Id_Required_is_false()
{
Assert.False(id.Required.HasValue);
}
+
+ [Fact]
+ public void Last_Login_Minimum_is_2010_1_23()
+ {
+ Assert.Equal(new DateTime(2010, 1, 23), lastLogin.GetRangeMinimum());
+ }
+
+ [Fact]
+ public void Last_Login_Maximum_is_DoomsDay()
+ {
+ Assert.Equal(DateTime.MaxValue, lastLogin.GetRangeMaximum());
+ }
+
+ [Fact]
+ public void Generic_bounceCount_rule_is_valid_when_email_has_bounced_twice()
+ {
+ var bounceCountRule = bounceCount.Rules
+ .OfType>()
+ .Single();
+ var webUser = new WebUser();
+
+ webUser.MailHasBounced();
+ webUser.MailHasBounced();
+
+ Assert.True(bounceCountRule.IsValid(webUser.BounceCount));
+ }
+
+ [Fact]
+ public void Generic_bounceCount_rule_is_invalid_when_email_has_bounced_thrice()
+ {
+ var bounceCountRule = bounceCount.Rules
+ .OfType>()
+ .Single();
+ var webUser = new WebUser();
+
+ webUser.MailHasBounced();
+ webUser.MailHasBounced();
+ webUser.MailHasBounced();
+
+ Console.WriteLine(bounceCountRule.FormatErrorMessage(bounceCount.GetDisplayName()));
+ Assert.False(bounceCountRule.IsValid(webUser.BounceCount));
+ }
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUserIndexModel.cs b/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUserIndexModel.cs
index 8f4c351..359dfc1 100644
--- a/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUserIndexModel.cs
+++ b/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUserIndexModel.cs
@@ -5,16 +5,16 @@ namespace FluentMetadata.Specs
{
public class PropertyMedata_with_WebUserIndexModel : MetadataTestBase
{
- private Metadata username;
- private Metadata id;
- private Metadata autorName;
+ Metadata username, id, autorName, email, role;
public PropertyMedata_with_WebUserIndexModel()
{
var query = new QueryFluentMetadata();
username = query.GetMetadataFor(typeof(WebUserIndexModel), "Username");
id = query.GetMetadataFor(typeof(WebUserIndexModel), "Id");
+ email = query.GetMetadataFor(typeof(WebUserIndexModel), "EMail");
autorName = query.GetMetadataFor(typeof(WebUserIndexModel), "AutorName");
+ role = query.GetMetadataFor(typeof(WebUserIndexModel), "Role");
}
[Fact]
@@ -32,7 +32,7 @@ public void Username_ModelType_is_string()
[Fact]
public void Username_DisplayName_is_Benutzername()
{
- Assert.Equal("Benutzername", username.DisplayName);
+ Assert.Equal("Benutzername", username.GetDisplayName());
}
[Fact]
@@ -62,7 +62,101 @@ public void Id_Required_is_false()
[Fact]
public void AutorName_DisplayName_is_emaN()
{
- Assert.Equal("emaN",autorName.DisplayName);
+ Assert.Equal("emaN", autorName.GetDisplayName());
+ }
+
+ [Fact]
+ public void EMail_DataTypeName_is_EmailAddress()
+ {
+ Assert.Equal("EmailAddress", email.DataTypeName);
+ }
+
+ [Fact]
+ public void Username_Description_is_Name_des_Benutzers()
+ {
+ Assert.Equal("Name des Benutzers", username.Description);
+ }
+
+ [Fact]
+ public void EMail_DisplayFormat_is_MailtoLink()
+ {
+ Assert.Equal("{0}", email.DisplayFormat);
+ }
+
+ [Fact]
+ public void EMail_EditorFormat_is_plain_value()
+ {
+ Assert.Equal("{0}", email.EditorFormat);
+ }
+
+ [Fact]
+ public void Id_HideSurroundingHtml_is_true()
+ {
+ Assert.True(id.HideSurroundingHtml.HasValue);
+ Assert.True(id.HideSurroundingHtml.Value);
+ }
+
+ [Fact]
+ public void Username_ReadOnly_is_true()
+ {
+ Assert.True(username.Readonly);
+ }
+
+ [Fact]
+ public void AutorName_NullDisplayText_is_Anonymous_Autor()
+ {
+ Assert.Equal("Anonymous Autor", autorName.NullDisplayText);
+ }
+
+ [Fact]
+ public void Id_ShowDisplay_is_false()
+ {
+ Assert.False(id.ShowDisplay);
+ }
+
+ [Fact]
+ public void Id_ShowEditor_is_false()
+ {
+ Assert.False(id.ShowEditor);
+ }
+
+ [Fact]
+ public void Role_TemplateHint_is_Roles()
+ {
+ Assert.Equal("Roles", role.TemplateHint);
+ }
+
+ [Fact]
+ public void EMail_Watermark_is_dummy_address()
+ {
+ Assert.Equal("john@doe.com", email.Watermark);
+ }
+
+ [Fact]
+ public void Username_ConvertEmptyStringToNull_is_false()
+ {
+ Assert.False(username.ConvertEmptyStringToNull);
+ }
+
+ [Fact]
+ public void Id_Hidden_is_true()
+ {
+ Assert.True(id.Hidden.HasValue);
+ Assert.True(id.Hidden.Value);
+ }
+
+ [Fact]
+ public void Username_GetMaximumLength_is_256()
+ {
+ var maxLength = username.GetMaximumLength();
+ Assert.True(maxLength.HasValue);
+ Assert.Equal(256, maxLength);
+ }
+
+ [Fact]
+ public void Username_ContainerType_is_WebUserIndexModel()
+ {
+ Assert.Equal(typeof(WebUserIndexModel), username.ContainerType);
}
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core.Specs/Rules/PropertyMustBeLessThanOtherRuleSpecs.cs b/Source/FluentMetadata.Core.Specs/Rules/PropertyMustBeLessThanOtherRuleSpecs.cs
new file mode 100644
index 0000000..5ba5fa4
--- /dev/null
+++ b/Source/FluentMetadata.Core.Specs/Rules/PropertyMustBeLessThanOtherRuleSpecs.cs
@@ -0,0 +1,69 @@
+using System;
+using FluentMetadata.Rules;
+using Xunit;
+
+namespace FluentMetadata.Specs.Rules
+{
+ public class If_a_date_should_be_before_another : InstanceContextSpecification>
+ {
+ protected override PropertyMustBeLessThanOtherRule CreateSut()
+ {
+ return new PropertyMustBeLessThanOtherRule(x => x.AlertDate, x => x.EventDate);
+ }
+
+ protected override void Because()
+ {
+ }
+
+ [Observation]
+ public void It_is_valid_if_it_is_earlier_than_the_other()
+ {
+ var model = new Reminder { EventDate = DateTime.Now.AddDays(1) };
+ model.AlertDate = model.EventDate.AddHours(-2);
+ Sut.IsValid(model).ShouldBeTrue();
+ }
+
+ [Observation]
+ public void It_is_invalid_if_it_is_equal_to_the_other()
+ {
+ var model = new Reminder { EventDate = DateTime.Now.AddDays(1) };
+ model.AlertDate = model.EventDate;
+ Sut.IsValid(model).ShouldBeFalse();
+ }
+
+ [Observation]
+ public void It_is_invalid_if_it_is_later_than_the_other()
+ {
+ var model = new Reminder { EventDate = DateTime.Now.AddDays(1) };
+ model.AlertDate = model.EventDate.AddHours(2);
+ Sut.IsValid(model).ShouldBeFalse();
+ }
+ }
+
+ public class If_an_int_property_should_be_less_than_another : InstanceContextSpecification>
+ {
+ protected override PropertyMustBeLessThanOtherRule CreateSut()
+ {
+ return new PropertyMustBeLessThanOtherRule(x => x.AlertDayOfWeek, x => x.EventDayOfWeek);
+ }
+
+ protected override void Because()
+ {
+ }
+
+ [Observation]
+ public void It_is_valid_if_it_is_less_than_the_other()
+ {
+ var model = new Reminder { EventDayOfWeek = 5, AlertDayOfWeek = 3 };
+ Sut.IsValid(model).ShouldBeTrue();
+ }
+ }
+
+ public class Reminder
+ {
+ public DateTime AlertDate { get; set; }
+ public DateTime EventDate { get; set; }
+ public int AlertDayOfWeek { get; set; }
+ public int EventDayOfWeek { get; set; }
+ }
+}
diff --git a/Source/FluentMetadata.Core.Specs/Rules/PropertyMustMatchRegexRuleSpecs.cs b/Source/FluentMetadata.Core.Specs/Rules/PropertyMustMatchRegexRuleSpecs.cs
new file mode 100644
index 0000000..deef4e2
--- /dev/null
+++ b/Source/FluentMetadata.Core.Specs/Rules/PropertyMustMatchRegexRuleSpecs.cs
@@ -0,0 +1,83 @@
+using FluentMetadata.Rules;
+using Xunit;
+
+namespace FluentMetadata.Specs.Rules
+{
+ [Concern(typeof(PropertyMustMatchRegexRule))]
+ public class When_property_value_should_match_a_regex : InstanceContextSpecification
+ {
+ protected override void Because()
+ {
+ }
+
+ protected override PropertyMustMatchRegexRule CreateSut()
+ {
+ //from http://regexlib.com/REDetails.aspx?regexp_id=96
+ const string validUri = @"(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?";
+ return new PropertyMustMatchRegexRule(validUri);
+ }
+
+ [Observation]
+ public void A_null_value_is_valid() // because to check this is the responsibility of the RequiredRule
+ {
+ Sut.IsValid(null).ShouldBeTrue();
+ }
+
+ [Observation]
+ public void A_value_matching_the_pattern_is_valid()
+ {
+ Sut.IsValid("http://regexlib.com/REDetails.aspx?regexp_id=96").ShouldBeTrue();
+ }
+
+ [Observation]
+ public void A_value_not_matching_the_pattern_is_invalid()
+ {
+ Sut.IsValid("regexlib.com/REDetails.aspx?regexp_id=96").ShouldBeFalse();
+ }
+
+ [Observation]
+ public void An_empty_string_value_is_valid() // because to check this is not the responsibility of the PropertyMustMatchRegexRule
+ {
+ Sut.IsValid(string.Empty).ShouldBeTrue();
+ }
+ }
+
+ [Concern(typeof(PropertyMustNotMatchRegexRule))]
+ public class When_property_value_should_not_match_a_regex : InstanceContextSpecification
+ {
+ protected override void Because()
+ {
+ }
+
+ protected override PropertyMustNotMatchRegexRule CreateSut()
+ {
+ //from http://regexlib.com/REDetails.aspx?regexp_id=96
+ const string validUri = @"(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?";
+ return new PropertyMustNotMatchRegexRule(validUri);
+ }
+
+ [Observation]
+ public void A_null_value_is_valid() // because to check this is the responsibility of the RequiredRule
+ {
+ Sut.IsValid(null).ShouldBeTrue();
+ }
+
+ [Observation]
+ public void A_value_matching_the_pattern_is_valid()
+ {
+ Sut.IsValid("http://regexlib.com/REDetails.aspx?regexp_id=96").ShouldBeFalse();
+ }
+
+ [Observation]
+ public void A_value_not_matching_the_pattern_is_invalid()
+ {
+ Sut.IsValid("regexlib.com/REDetails.aspx?regexp_id=96").ShouldBeTrue();
+ }
+
+ [Observation]
+ public void An_empty_string_value_is_valid() // because to check this is not the responsibility of the PropertyMustMatchRegexRule
+ {
+ Sut.IsValid(string.Empty).ShouldBeTrue();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core.Specs/Rules/PropertyMustMatchRuleSpecs.cs b/Source/FluentMetadata.Core.Specs/Rules/PropertyMustMatchRuleSpecs.cs
new file mode 100644
index 0000000..93d9499
--- /dev/null
+++ b/Source/FluentMetadata.Core.Specs/Rules/PropertyMustMatchRuleSpecs.cs
@@ -0,0 +1,38 @@
+using FluentMetadata.Rules;
+using Xunit;
+
+namespace FluentMetadata.Specs.Rules
+{
+ public class ChangePasswordModel
+ {
+ public string OldPassword { get; set; }
+ public string NewPassword { get; set; }
+ public string ConfirmPassword { get; set; }
+ }
+
+ public class When_two_properties_should_be_equal : InstanceContextSpecification>
+ {
+ protected override PropertyMustMatchRule CreateSut()
+ {
+ return new PropertyMustMatchRule(x => x.NewPassword, x => x.ConfirmPassword);
+ }
+
+ protected override void Because()
+ {
+ }
+
+ [Observation]
+ public void Should_be_valid_if_properties_match()
+ {
+ var model = new ChangePasswordModel { NewPassword = "asdf", ConfirmPassword = "asdf" };
+ Sut.IsValid(model).ShouldBeTrue();
+ }
+
+ [Observation]
+ public void Should_be_invalid_if_properties_do_not_match()
+ {
+ var model = new ChangePasswordModel { NewPassword = "qwer", ConfirmPassword = "asdf" };
+ Sut.IsValid(model).ShouldBeFalse();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core.Specs/Rules/StringLengthRuleSpecs.cs b/Source/FluentMetadata.Core.Specs/Rules/StringLengthRuleSpecs.cs
index ca13a79..e614e73 100644
--- a/Source/FluentMetadata.Core.Specs/Rules/StringLengthRuleSpecs.cs
+++ b/Source/FluentMetadata.Core.Specs/Rules/StringLengthRuleSpecs.cs
@@ -59,9 +59,94 @@ public void Should_Valid_with_a_string_with_length_0()
[Observation]
public void Should_Valid_with_a_string_is_NULL()
{
- string badLength = null;
+ string badLength = null;
Sut.IsValid(badLength).ShouldBeTrue();
}
+ }
+
+ public class When_the_minimal_StringLength_is_8_and_Maximal_StringLength_is_null : InstanceContextSpecification
+ {
+ protected override StringLengthRule CreateSut()
+ {
+ return new StringLengthRule(8, null);
+ }
+
+ protected override void Because()
+ {
+ }
+
+ [Observation]
+ public void Should_be_invalid_with_a_null_string()
+ {
+ Sut.IsValid(null).ShouldBeFalse();
+ }
+ [Observation]
+ public void Should_be_invalid_with_a_string_with_length_7()
+ {
+ var value = new string('a', 7);
+ Sut.IsValid(value).ShouldBeFalse();
+ }
+
+ [Observation]
+ public void Should_be_valid_with_a_string_with_length_8()
+ {
+ var value = new string('a', 8);
+ Sut.IsValid(value).ShouldBeTrue();
+ }
+
+ [Observation]
+ public void Should_be_valid_with_a_string_with_length_4001()
+ {
+ var value = new string('a', 4001);
+ Sut.IsValid(value).ShouldBeTrue();
+ }
+ }
+
+ public class When_the_minimal_StringLength_is_5_and_Maximal_StringLength_is_250 : InstanceContextSpecification
+ {
+ protected override StringLengthRule CreateSut()
+ {
+ return new StringLengthRule(5, 250);
+ }
+
+ protected override void Because()
+ {
+ }
+
+ [Observation]
+ public void Should_be_invalid_with_a_string_with_length_4()
+ {
+ var value = new string('a', 4);
+ Sut.IsValid(value).ShouldBeFalse();
+ }
+
+ [Observation]
+ public void Should_be_valid_with_a_string_with_length_5()
+ {
+ var value = new string('a', 5);
+ Sut.IsValid(value).ShouldBeTrue();
+ }
+
+ [Observation]
+ public void Should_be_valid_with_a_string_with_length_249()
+ {
+ var value = new string('a', 249);
+ Sut.IsValid(value).ShouldBeTrue();
+ }
+
+ [Observation]
+ public void Should_be_valid_with_a_string_with_length_250()
+ {
+ var value = new string('a', 249);
+ Sut.IsValid(value).ShouldBeTrue();
+ }
+
+ [Observation]
+ public void Should_be_invalid_with_a_string_with_length_251()
+ {
+ var value = new string('a', 251);
+ Sut.IsValid(value).ShouldBeFalse();
+ }
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/DomainObjectMetadata.cs b/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/DomainObjectMetadata.cs
index 65ed521..5084cef 100644
--- a/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/DomainObjectMetadata.cs
+++ b/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/DomainObjectMetadata.cs
@@ -4,9 +4,21 @@ public class DomainObjectMetadata : ClassMetadata where T : DomainObject
{
protected DomainObjectMetadata()
{
- Property(x => x.Id).Should.HiddenInput().Should.Not.ShowInEditor().Should.Not.ShowInDisplay().Is.ReadOnly();
- Property(x => x.Created).Is.Required().Should.Not.ShowInEditor().Display.Name("Angelegt").Is.ReadOnly();
- Property(x => x.Updated).Is.Required().Should.Not.ShowInEditor().Display.Name("Bearbeitet").Is.ReadOnly();
+ Property(x => x.Id)
+ .Should.HiddenInput()
+ .Should.Not.ShowInEditor()
+ .Should.Not.ShowInDisplay()
+ .Is.ReadOnly();
+ Property(x => x.Created)
+ .Is.Required()
+ .Should.Not.ShowInEditor()
+ .Display.Name("Angelegt")
+ .Is.ReadOnly();
+ Property(x => x.Updated)
+ .Is.Required()
+ .Should.Not.ShowInEditor()
+ .Display.Name("Bearbeitet")
+ .Is.ReadOnly();
}
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/PersonMetadata.cs b/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/PersonMetadata.cs
index 6490b72..b3f297d 100644
--- a/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/PersonMetadata.cs
+++ b/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/PersonMetadata.cs
@@ -4,9 +4,12 @@ public class PersonMetadata : ClassMetadata
{
public PersonMetadata()
{
- Property(p => p.FirstName).Is.Required();
- Class.Display.Name("Benutzer");
- Class.Display.Format("{0} der Benutzer");
+ Property(p => p.FirstName)
+ .Is.Required();
+ Class
+ .Display.Name("Benutzer")
+ .Display.Format("{0} der Benutzer")
+ .Property(p => p.FirstName).ShouldEqual(p => p.LastName);
}
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/WebUserMetadata.cs b/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/WebUserMetadata.cs
index 8376a6d..24e8991 100644
--- a/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/WebUserMetadata.cs
+++ b/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/WebUserMetadata.cs
@@ -1,28 +1,68 @@
+using System;
+
namespace FluentMetadata.Specs.SampleClasses.MetaData
{
- public class WebUserMetadata : DomainObjectMetadata
+ public class WebUserMetadata : DomainObjectMetadata
{
public WebUserMetadata()
{
- Property(x => x.Username).Length(256).Is.Required().Is.ReadOnly()
- .Display.Name("Benutzername");
- Property(x => x.EMail).Length(128).Is.Required().As.EmailAddress()
- .Display.Name("E-Mail").As.EmailAddress();
- Property(x => x.PasswordHash).Length(64).Is.Required()
- .Should.Not.ShowInDisplay().Should.Not.ShowInEditor();
- Property(x => x.Role).UIHint("Roles").Length(256).Is.Required()
+ Property(x => x.Username)
+ .Length(3, 256)
+ .Is.Required()
+ .Is.ReadOnly()
+ .Display.Name("Benutzername")
+ .Description("Name des Benutzers")
+ .Is.Not.ConvertEmptyStringToNull();
+ Property(x => x.EMail)
+ .Length(128)
+ .Is.Required()
+ .As.EmailAddress()
+ .Display.Name("E-Mail")
+ .Display.Format("{0}")
+ .Editor.Format("{0}")
+ .Editor.Watermark("john@doe.com");
+ Property(x => x.PasswordHash)
+ .Length(32, null)
+ .Is.Required()
+ .Should.Not.ShowInDisplay()
+ .Should.Not.ShowInEditor();
+ Property(x => x.Role)
+ .UIHint("Roles")
+ .Length(256)
+ .Is.Required()
.Display.Name("Rolle");
- Property(x => x.PasswordHash).Should.Not.ShowInDisplay().Should.Not.ShowInEditor();
- Property(x => x.ConfirmationKey).Should.Not.ShowInEditor().Should.Not.ShowInDisplay();
- Property(x => x.LastLogin).Should.Not.ShowInEditor()
- .Display.Name("Letzte Anmeldung").Display.NullText("");
- Property(x => x.BounceCount).Should.Not.ShowInEditor()
- .Display.Name("E-Mail Fehler");
- Property(x => x.Confirmed).Should.Not.HiddenInput()
+ Property(x => x.PasswordHash)
+ .Should.Not.ShowInDisplay()
+ .Should.Not.ShowInEditor();
+ Property(x => x.ConfirmationKey)
+ .Should.Not.ShowInEditor()
+ .Should.Not.ShowInDisplay();
+ Property(x => x.LastLogin)
+ .Should.Not.ShowInEditor()
+ .Display.Name("Letzte Anmeldung")
+ .Display.NullText("")
+ .Range(new DateTime(2010, 1, 23), DateTime.MaxValue); //support ends on doomsday
+ Property(x => x.BounceCount)
+ .Should.Not.ShowInEditor()
+ .Display.Name("E-Mail Fehler")
+ .AssertThat(
+ bc => ValidateBounceCountAgainstSomeConfiguration(bc),
+ "{0} is too high. Email address is considered invalid.");
+ Property(x => x.Confirmed)
+ .Should.Not.HiddenInput()
.Display.Name("Bestätigt");
Property(x => x.Active)
.Display.Name("Aktiv");
- Class.Display.Name("Benutzer");
+ Class
+ .Display.Name("Benutzer")
+ .AssertThat(
+ u => u.Username != u.Autor.Name,
+ "{0}.Username and {0}.Autor.Name must not be equal");
+ }
+
+ bool ValidateBounceCountAgainstSomeConfiguration(int bounceCount)
+ {
+ return bounceCount < 3;
}
}
@@ -30,7 +70,9 @@ public class AutorMetadata : DomainObjectMetadata
{
public AutorMetadata()
{
- Property(e => e.Name).Display.Name("emaN");
+ Property(e => e.Name)
+ .Display.Name("emaN")
+ .Display.NullText("Anonymous Autor");
}
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core.Specs/SampleClasses/WebUser.cs b/Source/FluentMetadata.Core.Specs/SampleClasses/WebUser.cs
index f530437..424d1ae 100644
--- a/Source/FluentMetadata.Core.Specs/SampleClasses/WebUser.cs
+++ b/Source/FluentMetadata.Core.Specs/SampleClasses/WebUser.cs
@@ -9,11 +9,11 @@ public class Autor : DomainObject
public class WebUser : DomainObject
{
- private WebUser()
+ internal WebUser()
{
}
- public override void Initialize()
+ public override void Initialize()
{
base.Initialize();
PasswordHash = string.Empty;
@@ -21,7 +21,7 @@ public override void Initialize()
Active = false;
}
- public string Username { get; private set; }
+ public string Username { get; internal set; }
public string EMail { get; private set; }
@@ -33,7 +33,7 @@ public override void Initialize()
public DateTime? LastLogin { get; private set; }
public Guid? ConfirmationKey { get; private set; }
public string Role { get; private set; }
- public Autor Autor { get; private set; }
+ public Autor Autor { get; internal set; }
public void SetEMailAddress(string emailAddress)
{
@@ -48,7 +48,7 @@ public void SetEMailAddress(string emailAddress)
private string GetUserSalt()
{
- return (Created.Second*Created.DayOfYear).ToString();
+ return (Created.Second * Created.DayOfYear).ToString();
}
public void MailHasBounced()
diff --git a/Source/FluentMetadata.Core/Builder/AsBuilder.cs b/Source/FluentMetadata.Core/Builder/AsBuilder.cs
index 3c08180..0732289 100644
--- a/Source/FluentMetadata.Core/Builder/AsBuilder.cs
+++ b/Source/FluentMetadata.Core/Builder/AsBuilder.cs
@@ -11,7 +11,7 @@ public AsBuilder(PropertyMetadataBuilder propertyMetaDataBuilder)
this.propertyMetaDataBuilder = propertyMetaDataBuilder;
}
- public IProperty EmailAddress()
+ public IProperty EmailAddress()
{
SetDataTypeName(DataType.EmailAddress);
return propertyMetaDataBuilder;
@@ -22,34 +22,40 @@ private void SetDataTypeName(DataType dataType)
propertyMetaDataBuilder.Metadata.DataTypeName = dataType.ToString();
}
- public IProperty Url()
+ public IProperty Url()
{
SetDataTypeName(DataType.Url);
return propertyMetaDataBuilder;
}
- public IProperty Html()
+ public IProperty Html()
{
SetDataTypeName(DataType.Html);
return propertyMetaDataBuilder;
}
- public IProperty Text()
+ public IProperty Text()
{
SetDataTypeName(DataType.Text);
return propertyMetaDataBuilder;
}
- public IProperty MultilineText()
+ public IProperty MultilineText()
{
SetDataTypeName(DataType.MultilineText);
return propertyMetaDataBuilder;
}
- public IProperty Password()
+ public IProperty Password()
{
SetDataTypeName(DataType.Password);
return propertyMetaDataBuilder;
}
+
+ public IProperty Custom(string dataTypeName)
+ {
+ propertyMetaDataBuilder.Metadata.DataTypeName = dataTypeName;
+ return propertyMetaDataBuilder;
+ }
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core/Builder/ClassMetadataBuilder.cs b/Source/FluentMetadata.Core/Builder/ClassMetadataBuilder.cs
index f6bb82b..525726d 100644
--- a/Source/FluentMetadata.Core/Builder/ClassMetadataBuilder.cs
+++ b/Source/FluentMetadata.Core/Builder/ClassMetadataBuilder.cs
@@ -1,4 +1,6 @@
using System;
+using System.Linq.Expressions;
+using FluentMetadata.Rules;
namespace FluentMetadata.Builder
{
@@ -11,18 +13,16 @@ internal class ClassMetadataBuilder : IClassBuilder
public ClassMetadataBuilder(Metadata metadata)
{
this.metadata = metadata;
- metadata.ModelType = typeof (T);
-// metadata.ModelName = typeof (T).Name;
+ metadata.ModelType = typeof(T);
InitPropertyMetadata();
}
private void InitPropertyMetadata()
{
- string a;
- var builder = FluentMetadataBuilder.GetTypeBuilder(typeof (T));
- foreach (var propertyInfo in typeof (T).GetProperties())
+ var builder = FluentMetadataBuilder.GetTypeBuilder(typeof(T));
+ foreach (var propertyInfo in typeof(T).GetProperties())
{
- if (propertyInfo.GetIndexParameters().Length==0)
+ if (propertyInfo.GetIndexParameters().Length == 0)
{
var propertyMetadata = builder.MapProperty(typeof(T), propertyInfo.Name, propertyInfo.PropertyType);
metadata.Properties.Add(propertyMetadata);
@@ -44,5 +44,21 @@ public Metadata Metadata
{
get { return metadata; }
}
+
+ public IClassBuilder AssertThat(Func assertFunc, string errorMessageFormat)
+ {
+ metadata.AddRule(new GenericClassRule(errorMessageFormat, assertFunc));
+ return this;
+ }
+
+ public IPropertiesInClassContextBuilder Property(Expression> propertyExpression)
+ {
+ return new PropertiesInClassContextBuilder(this, propertyExpression);
+ }
+
+ public IComparablePropertiesInClassContextBuilder ComparableProperty(Expression> propertyExpression)
+ {
+ return new ComparablePropertiesInClassContextBuilder(this, propertyExpression);
+ }
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core/Builder/DisplayBuilder.cs b/Source/FluentMetadata.Core/Builder/DisplayBuilder.cs
index df7d747..101e890 100644
--- a/Source/FluentMetadata.Core/Builder/DisplayBuilder.cs
+++ b/Source/FluentMetadata.Core/Builder/DisplayBuilder.cs
@@ -1,4 +1,6 @@
-namespace FluentMetadata.Builder
+using System;
+
+namespace FluentMetadata.Builder
{
internal class DisplayBuilder : IDisplayClass
{
@@ -11,7 +13,13 @@ public DisplayBuilder(IClassBuilder classBuilder)
public IClassBuilder Name(string displayName)
{
- classBuilder.Metadata.DisplayName = displayName;
+ classBuilder.Metadata.DisplayNameFunc = () => displayName;
+ return classBuilder;
+ }
+
+ public IClassBuilder Name(Func displayNameFunc)
+ {
+ classBuilder.Metadata.DisplayNameFunc = displayNameFunc;
return classBuilder;
}
@@ -22,28 +30,34 @@ public IClassBuilder Format(string displayFormat)
}
}
- internal class DisplayBuilder : IDisplayProperty
+ internal class DisplayBuilder : IDisplayProperty
{
- private readonly PropertyMetadataBuilder propertyMetaDataBuilder;
+ private readonly PropertyMetadataBuilder propertyMetaDataBuilder;
- public DisplayBuilder(PropertyMetadataBuilder propertyMetaDataBuilder)
+ public DisplayBuilder(PropertyMetadataBuilder propertyMetaDataBuilder)
{
this.propertyMetaDataBuilder = propertyMetaDataBuilder;
}
- public IProperty NullText(string nullDisplayText)
+ public IProperty NullText(string nullDisplayText)
{
propertyMetaDataBuilder.Metadata.NullDisplayText = nullDisplayText;
return propertyMetaDataBuilder;
}
- public IProperty Name(string displayName)
+ public IProperty Name(string displayName)
+ {
+ propertyMetaDataBuilder.Metadata.DisplayNameFunc = () => displayName;
+ return propertyMetaDataBuilder;
+ }
+
+ public IProperty Name(Func displayNameFunc)
{
- propertyMetaDataBuilder.Metadata.DisplayName = displayName;
+ propertyMetaDataBuilder.Metadata.DisplayNameFunc = displayNameFunc;
return propertyMetaDataBuilder;
}
- public IProperty Format(string displayFormat)
+ public IProperty Format(string displayFormat)
{
propertyMetaDataBuilder.Metadata.DisplayFormat = displayFormat;
return propertyMetaDataBuilder;
diff --git a/Source/FluentMetadata.Core/Builder/ITypeMetadataBuilder.cs b/Source/FluentMetadata.Core/Builder/ITypeMetadataBuilder.cs
deleted file mode 100644
index c698f3b..0000000
--- a/Source/FluentMetadata.Core/Builder/ITypeMetadataBuilder.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-using System;
-using System.Linq.Expressions;
-
-namespace FluentMetadata.Builder
-{
-}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core/Builder/IsBuilder.cs b/Source/FluentMetadata.Core/Builder/IsBuilder.cs
index d6b5899..a87cad7 100644
--- a/Source/FluentMetadata.Core/Builder/IsBuilder.cs
+++ b/Source/FluentMetadata.Core/Builder/IsBuilder.cs
@@ -17,7 +17,7 @@ public IsBuilder(PropertyMetadataBuilder propertyMetaDataBuilder)
this.propertyMetaDataBuilder = propertyMetaDataBuilder;
}
- public IProperty Required()
+ public IProperty Required()
{
Metadata.Required = !notted;
notted = false;
@@ -28,7 +28,7 @@ public IProperty Required()
return propertyMetaDataBuilder;
}
- public IProperty ReadOnly()
+ public IProperty ReadOnly()
{
Metadata.Readonly = !notted;
notted = false;
@@ -44,5 +44,12 @@ public IIsNotProperty Not
return this;
}
}
+
+ public IProperty ConvertEmptyStringToNull()
+ {
+ Metadata.ConvertEmptyStringToNull = !notted;
+ notted = false;
+ return propertyMetaDataBuilder;
+ }
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core/Builder/PropertiesInClassContextBuilder.cs b/Source/FluentMetadata.Core/Builder/PropertiesInClassContextBuilder.cs
new file mode 100644
index 0000000..334bbdc
--- /dev/null
+++ b/Source/FluentMetadata.Core/Builder/PropertiesInClassContextBuilder.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Linq.Expressions;
+using FluentMetadata.Rules;
+
+namespace FluentMetadata.Builder
+{
+ internal class PropertiesInClassContextBuilder : IPropertiesInClassContextBuilder
+ {
+ readonly IClassBuilder classBuilder;
+ readonly Expression> propertyExpression;
+
+ public PropertiesInClassContextBuilder(IClassBuilder classBuilder, Expression> propertyExpression)
+ {
+ this.classBuilder = classBuilder;
+ this.propertyExpression = propertyExpression;
+ }
+
+ public IClassBuilder ShouldEqual(Expression> otherPropertyExpression)
+ {
+ classBuilder.Metadata.AddRule(new PropertyMustMatchRule(propertyExpression, otherPropertyExpression));
+ return classBuilder;
+ }
+ }
+
+ internal class ComparablePropertiesInClassContextBuilder : IComparablePropertiesInClassContextBuilder
+ {
+ readonly IClassBuilder classBuilder;
+ readonly Expression> propertyExpression;
+
+ public ComparablePropertiesInClassContextBuilder(IClassBuilder classBuilder, Expression> propertyExpression)
+ {
+ this.classBuilder = classBuilder;
+ this.propertyExpression = propertyExpression;
+ }
+
+ public IClassBuilder ShouldBeLessThan(Expression> otherPropertyExpression)
+ {
+ classBuilder.Metadata.AddRule(new PropertyMustBeLessThanOtherRule(propertyExpression, otherPropertyExpression));
+ return classBuilder;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core/Builder/PropertyMetadataBuilder.cs b/Source/FluentMetadata.Core/Builder/PropertyMetadataBuilder.cs
index 5667206..1e99233 100644
--- a/Source/FluentMetadata.Core/Builder/PropertyMetadataBuilder.cs
+++ b/Source/FluentMetadata.Core/Builder/PropertyMetadataBuilder.cs
@@ -8,7 +8,8 @@ internal abstract class PropertyMetadataBuilder
{
private readonly Metadata metadata;
- protected PropertyMetadataBuilder() : this(new Metadata())
+ protected PropertyMetadataBuilder()
+ : this(new Metadata())
{
}
@@ -23,9 +24,8 @@ public Metadata Metadata
}
}
- internal class PropertyMetadataBuilder : PropertyMetadataBuilder, IProperty
+ internal class PropertyMetadataBuilder : PropertyMetadataBuilder, IProperty
{
-
public PropertyMetadataBuilder(Metadata metadata)
: base(metadata)
{
@@ -33,45 +33,47 @@ public PropertyMetadataBuilder(Metadata metadata)
public PropertyMetadataBuilder(Expression> expression)
{
- Metadata.ContainerType = typeof (T);
+ Metadata.ContainerType = typeof(T);
Metadata.ModelName = ExpressionHelper.GetPropertyName(expression);
Metadata.ModelType = ExpressionHelper.GetPropertyType(expression);
}
-
- public PropertyMetadataBuilder(string propertyName)
+ public IProperty AssertThat(Func assertFunc, string errorMessageFormat)
{
- Metadata.ContainerType = null;
- Metadata.ModelName = propertyName;
- Metadata.ModelType = typeof (T);
+ Metadata.AddRule(new GenericRule(errorMessageFormat, assertFunc));
+ return this;
}
+ public IProperty Length(int maxLength)
+ {
+ Metadata.AddRule(new StringLengthRule(maxLength));
+ return this;
+ }
- public IProperty Length(int length)
+ public IProperty Length(int minLength, int? maxLength)
{
- Metadata.StringLength = length;
- Metadata.AddRule(new StringLengthRule(length));
+ Metadata.AddRule(new StringLengthRule(minLength, maxLength));
return this;
}
- public IProperty UIHint(string templateHint)
+ public IProperty UIHint(string templateHint)
{
Metadata.TemplateHint = templateHint;
return this;
}
- public IProperty Description(string description)
+ public IProperty Description(string description)
{
Metadata.Description = description;
return this;
}
- public IEditorProperty Editor
+ public IEditorProperty Editor
{
get { return new EditorBuilder(this); }
}
- public IDisplayProperty Display
+ public IDisplayProperty Display
{
get { return new DisplayBuilder(this); }
}
@@ -90,5 +92,11 @@ public IShouldProperty Should
{
get { return new ShouldBuilder(this); }
}
+
+ public IProperty Range(IComparable minimum, IComparable maximum)
+ {
+ Metadata.AddRule(new RangeRule(minimum, maximum));
+ return this;
+ }
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core/Builder/ShouldBuilder.cs b/Source/FluentMetadata.Core/Builder/ShouldBuilder.cs
index c1d34fb..d1132e0 100644
--- a/Source/FluentMetadata.Core/Builder/ShouldBuilder.cs
+++ b/Source/FluentMetadata.Core/Builder/ShouldBuilder.cs
@@ -1,4 +1,6 @@
-namespace FluentMetadata.Builder
+using FluentMetadata.Rules;
+
+namespace FluentMetadata.Builder
{
internal class ShouldBuilder : IShouldProperty
{
@@ -12,7 +14,7 @@ public ShouldBuilder(PropertyMetadataBuilder propertyMetaDataBuilder
private Metadata Metadata { get { return propertyMetaDataBuilder.Metadata; } }
- public IProperty HiddenInput()
+ public IProperty HiddenInput()
{
Metadata.Hidden = !notted;
Metadata.HideSurroundingHtml = !notted;
@@ -20,27 +22,36 @@ public IProperty HiddenInput()
return propertyMetaDataBuilder;
}
- public IProperty ShowInDisplay()
+ public IProperty ShowInDisplay()
{
Metadata.ShowDisplay = !notted;
notted = false;
return propertyMetaDataBuilder;
}
- public IProperty ShowInEditor()
+ public IProperty ShowInEditor()
{
Metadata.ShowEditor = !notted;
notted = false;
return propertyMetaDataBuilder;
}
- public IProperty HideSurroundingHtml()
+ public IProperty HideSurroundingHtml()
{
Metadata.HideSurroundingHtml = !notted;
notted = false;
return propertyMetaDataBuilder;
}
+ public IProperty MatchRegex(string pattern)
+ {
+ Metadata.AddRule(notted ?
+ new PropertyMustNotMatchRegexRule(pattern) :
+ new PropertyMustMatchRegexRule(pattern));
+ notted = false;
+ return propertyMetaDataBuilder;
+ }
+
public IShouldNotProperty Not
{
get
diff --git a/Source/FluentMetadata.Core/Builder/TypeMetaDataBuilder.cs b/Source/FluentMetadata.Core/Builder/TypeMetaDataBuilder.cs
index fdeb2e6..92de2d5 100644
--- a/Source/FluentMetadata.Core/Builder/TypeMetaDataBuilder.cs
+++ b/Source/FluentMetadata.Core/Builder/TypeMetaDataBuilder.cs
@@ -36,8 +36,15 @@ protected bool TryGetPropertyBuilder(string propertyName, out PropertyMetadataBu
public Metadata MapProperty(Type containerType, string propertyName, Type propertyType)
{
- var newMetaData = new Metadata() { ContainerType = containerType, ModelName = propertyName,ModelType = propertyType};
- return MapProperty(containerType, propertyName, newMetaData);
+ return MapProperty(
+ containerType,
+ propertyName,
+ new Metadata
+ {
+ ContainerType = containerType,
+ ModelName = propertyName,
+ ModelType = propertyType
+ });
}
public abstract void Init();
@@ -50,11 +57,7 @@ public IProperty MapProperty(Expression> e
return GetBuilder(expression);
}
- public TypeMetadataBuilder()
- {
- }
-
- private PropertyMetadataBuilder GetBuilder(Expression> expression)
+ PropertyMetadataBuilder GetBuilder(Expression> expression)
{
string propertyName = ExpressionHelper.GetPropertyName(expression);
@@ -64,7 +67,7 @@ private PropertyMetadataBuilder GetBuilder(Expression(expression);
PropertyBuilders.Add(propertyBuilder);
}
- return (PropertyMetadataBuilder) propertyBuilder;
+ return (PropertyMetadataBuilder)propertyBuilder;
}
public override Metadata MapProperty(Type containerType, string propertyName, Metadata metadata)
@@ -85,28 +88,13 @@ public override void Init()
ClassBuilder();
}
- private PropertyMetadataBuilder CreatePropertyMetaDataBuilder(Metadata metadata, Type containerType,
- Metadata newMetadata)
- {
- return (PropertyMetadataBuilder) typeof (PropertyMetadataBuilder<,>)
- .CreateGenericInstance(containerType, metadata.ModelType, newMetadata);
- }
-
- public IProperty MapEnum(object value)
+ PropertyMetadataBuilder CreatePropertyMetaDataBuilder(Metadata metadata, Type containerType, Metadata newMetadata)
{
- string propertyName = Enum.GetName(typeof (TResult), value);
- PropertyMetadataBuilder builder;
- if (!TryGetPropertyBuilder(propertyName, out builder))
- {
- builder = new PropertyMetadataBuilder(propertyName);
- PropertyBuilders.Add(builder);
- }
- return (IProperty) builder;
+ return (PropertyMetadataBuilder)typeof(PropertyMetadataBuilder<,>)
+ .CreateGenericInstance(containerType, metadata.ModelType, newMetadata);
}
-
- private IClassBuilder classBuilder;
-
+ IClassBuilder classBuilder;
public IClassBuilder ClassBuilder()
{
if (classBuilder == null)
diff --git a/Source/FluentMetadata.Core/ClassMetadata.cs b/Source/FluentMetadata.Core/ClassMetadata.cs
index 5e57fb3..6f25944 100644
--- a/Source/FluentMetadata.Core/ClassMetadata.cs
+++ b/Source/FluentMetadata.Core/ClassMetadata.cs
@@ -1,7 +1,6 @@
using System;
using System.Linq.Expressions;
using FluentMetadata.Builder;
-using FluentMetadata.Rules;
namespace FluentMetadata
{
@@ -11,9 +10,12 @@ internal interface IClassMetadata
public abstract class ClassMetadata : IClassMetadata
{
- protected ClassMetadata()
+ protected IClassBuilder Class
{
- GetTypeBuilder().ClassBuilder();
+ get
+ {
+ return GetTypeBuilder().ClassBuilder();
+ }
}
protected IProperty Property(Expression> expression)
@@ -21,30 +23,14 @@ protected IProperty Property(Expression> e
return GetTypeBuilder().MapProperty(expression);
}
- protected IProperty Property(T value)
- {
- return GetTypeBuilder().MapEnum(value);
- }
-
- protected IClassBuilder Class
- {
- get { return GetTypeBuilder().ClassBuilder(); }
- }
-
- protected void ClassRule(IClassRule classRule)
- {
- var typeBuilder = GetTypeBuilder();
- typeBuilder.Metadata.AddRule(classRule);
- }
-
protected void CopyMetadataFrom()
{
- MetadataHelper.CopyMetadata(typeof (TBaseType), typeof (T));
+ MetadataHelper.CopyMetadata(typeof(TBaseType), typeof(T));
}
- private static TypeMetadataBuilder GetTypeBuilder()
+ static TypeMetadataBuilder GetTypeBuilder()
{
- return (TypeMetadataBuilder) FluentMetadataBuilder.GetTypeBuilder();
+ return FluentMetadataBuilder.GetTypeBuilder();
}
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core/FluentMetadata.Core.csproj b/Source/FluentMetadata.Core/FluentMetadata.Core.csproj
index 7b43fc4..1d8d8aa 100644
--- a/Source/FluentMetadata.Core/FluentMetadata.Core.csproj
+++ b/Source/FluentMetadata.Core/FluentMetadata.Core.csproj
@@ -70,13 +70,17 @@
-
+
+
+
+
+
@@ -95,6 +99,7 @@
+
diff --git a/Source/FluentMetadata.Core/IAsProperty.cs b/Source/FluentMetadata.Core/IAsProperty.cs
index 041217f..e46c815 100644
--- a/Source/FluentMetadata.Core/IAsProperty.cs
+++ b/Source/FluentMetadata.Core/IAsProperty.cs
@@ -2,11 +2,12 @@
{
public interface IAsProperty
{
- IProperty EmailAddress();
- IProperty Url();
- IProperty Html();
- IProperty Text();
- IProperty MultilineText();
- IProperty Password();
+ IProperty EmailAddress();
+ IProperty Url();
+ IProperty Html();
+ IProperty Text();
+ IProperty MultilineText();
+ IProperty Password();
+ IProperty Custom(string dataTypeName);
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core/IClassBuilder.cs b/Source/FluentMetadata.Core/IClassBuilder.cs
index 98f0cae..b88d8ba 100644
--- a/Source/FluentMetadata.Core/IClassBuilder.cs
+++ b/Source/FluentMetadata.Core/IClassBuilder.cs
@@ -1,8 +1,36 @@
-namespace FluentMetadata
+using System;
+using System.Linq.Expressions;
+
+namespace FluentMetadata
{
public interface IClassBuilder
{
Metadata Metadata { get; }
IDisplayClass Display { get; }
+
+ ///
+ /// Creates a generic class rule (i.e. a rule that is evaluated in class context)
+ /// asserting that the returns true.
+ ///
+ /// What to assert.
+ /// The error message format. Can contain a placeholder for {0} the the class display name.
+ ///
+ IClassBuilder AssertThat(Func assertFunc, string errorMessageFormat);
+
+ ///
+ /// Entry point for class rules (i.e. rules that must be evaluated in class context)
+ /// that concern more than one property.
+ ///
+ /// The property expression.
+ ///
+ IPropertiesInClassContextBuilder Property(Expression> propertyExpression);
+
+ ///
+ /// Entry point for class rules (i.e. rules that must be evaluated in class context)
+ /// that concern more than one property and need to compare properties.
+ ///
+ /// The property expression.
+ ///
+ IComparablePropertiesInClassContextBuilder ComparableProperty(Expression> propertyExpression);
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core/IDisplayClass.cs b/Source/FluentMetadata.Core/IDisplayClass.cs
index e8e96b7..d116774 100644
--- a/Source/FluentMetadata.Core/IDisplayClass.cs
+++ b/Source/FluentMetadata.Core/IDisplayClass.cs
@@ -1,8 +1,25 @@
-namespace FluentMetadata
+using System;
+
+namespace FluentMetadata
{
public interface IDisplayClass
{
+ ///
+ /// Sets the display name of the class.
+ /// Use this for static, i.e. culture invariant display names.
+ ///
+ /// The display name.
+ ///
IClassBuilder Name(string displayName);
+
+ ///
+ /// Sets the display name of the class.
+ /// Use this for dynamic, i.e. localized display names, e.g. resource strings.
+ ///
+ /// The display name.
+ ///
+ IClassBuilder Name(Func displayNameFunc);
+
IClassBuilder Format(string displayFormat);
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core/IDisplayProperty.cs b/Source/FluentMetadata.Core/IDisplayProperty.cs
index e605583..f098a99 100644
--- a/Source/FluentMetadata.Core/IDisplayProperty.cs
+++ b/Source/FluentMetadata.Core/IDisplayProperty.cs
@@ -1,9 +1,27 @@
-namespace FluentMetadata
+using System;
+
+namespace FluentMetadata
{
- public interface IDisplayProperty
+ public interface IDisplayProperty
{
- IProperty NullText(string nullDisplayText);
- IProperty Name(string displayName);
- IProperty Format(string displayFormat);
+ IProperty NullText(string nullDisplayText);
+
+ ///
+ /// Sets the display name of the property.
+ /// Use this for static, i.e. culture invariant display names.
+ ///
+ /// The display name.
+ ///
+ IProperty Name(string displayName);
+
+ ///
+ /// Sets the display name of the property.
+ /// Use this for dynamic, i.e. localized display names, e.g. resource strings.
+ ///
+ /// The display name.
+ ///
+ IProperty Name(Func displayNameFunc);
+
+ IProperty Format(string displayFormat);
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core/IIsProperty.cs b/Source/FluentMetadata.Core/IIsProperty.cs
index d7b9cdf..a698d97 100644
--- a/Source/FluentMetadata.Core/IIsProperty.cs
+++ b/Source/FluentMetadata.Core/IIsProperty.cs
@@ -1,16 +1,14 @@
-using System;
-using System.Linq.Expressions;
-
-namespace FluentMetadata
+namespace FluentMetadata
{
public interface IIsProperty : IIsNotProperty
{
IIsNotProperty Not { get; }
}
- public interface IIsNotProperty
+ public interface IIsNotProperty
{
- IProperty Required();
- IProperty ReadOnly();
+ IProperty Required();
+ IProperty ReadOnly();
+ IProperty ConvertEmptyStringToNull();
}
}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core/IPropertiesInClassContextBuilder.cs b/Source/FluentMetadata.Core/IPropertiesInClassContextBuilder.cs
new file mode 100644
index 0000000..119c239
--- /dev/null
+++ b/Source/FluentMetadata.Core/IPropertiesInClassContextBuilder.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Linq.Expressions;
+
+namespace FluentMetadata
+{
+ ///
+ /// Entry point for class rules (i.e. rules that must be evaluated in class context)
+ /// that concern more than one property.
+ ///
+ public interface IPropertiesInClassContextBuilder
+ {
+ ///
+ /// Adds a rule validating that the property's value is equal to the value of another property.
+ ///
+ /// The other property expression.
+ ///
+ IClassBuilder ShouldEqual(Expression> otherPropertyExpression);
+ }
+
+ ///
+ /// Entry point for class rules (i.e. rules that must be evaluated in class context)
+ /// that concern more than one property and need to compare properties.
+ ///
+ public interface IComparablePropertiesInClassContextBuilder
+ {
+ ///
+ /// Adds a rule validating that the property's value is less than the value of another property.
+ ///
+ /// The other property expression.
+ ///
+ IClassBuilder ShouldBeLessThan(Expression> otherPropertyExpression);
+ }
+}
\ No newline at end of file
diff --git a/Source/FluentMetadata.Core/IProperty.cs b/Source/FluentMetadata.Core/IProperty.cs
index 9d9c98f..73debd1 100644
--- a/Source/FluentMetadata.Core/IProperty.cs
+++ b/Source/FluentMetadata.Core/IProperty.cs
@@ -1,14 +1,17 @@
-namespace FluentMetadata
+using System;
+
+namespace FluentMetadata
{
- public interface IProperty