diff --git a/README.md b/README.md
index 5cf4dc6..c6caeb2 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,7 @@ Currently the following is supported:
- Not Null constraint
- Auto increment (An int PrimaryKey will automatically be incremented)
- Index (Decorate columns with the `Index` attribute. Indices are automatically created for foreign keys by default. To prevent this you can remove the convetion `ForeignKeyIndexConvention`)
+- Unique constraint (Decorate columsn with the `UniqueAttribute` which is part of this library)
I tried to write the code in a extensible way.
The logic is divided into two main parts, Builder and Statement.
diff --git a/SQLite.CodeFirst.Console/Entity/Player.cs b/SQLite.CodeFirst.Console/Entity/Player.cs
index 8e69fab..e2177cf 100644
--- a/SQLite.CodeFirst.Console/Entity/Player.cs
+++ b/SQLite.CodeFirst.Console/Entity/Player.cs
@@ -6,6 +6,7 @@ namespace SQLite.CodeFirst.Console.Entity
public class Player : Person
{
[Index] // Automatically named 'IX_TeamPlayer_Number'
+ [Unique(OnConflictAction.Fail)]
public int Number { get; set; }
public virtual Team Team { get; set; }
diff --git a/SQLite.CodeFirst.Test/SQLite.CodeFirst.Test.csproj b/SQLite.CodeFirst.Test/SQLite.CodeFirst.Test.csproj
index a22972a..dfb57d2 100644
--- a/SQLite.CodeFirst.Test/SQLite.CodeFirst.Test.csproj
+++ b/SQLite.CodeFirst.Test/SQLite.CodeFirst.Test.csproj
@@ -80,6 +80,7 @@
+
diff --git a/SQLite.CodeFirst.Test/Statement/ColumnConstraint/UniqueConstraintTest.cs b/SQLite.CodeFirst.Test/Statement/ColumnConstraint/UniqueConstraintTest.cs
new file mode 100644
index 0000000..198c9cb
--- /dev/null
+++ b/SQLite.CodeFirst.Test/Statement/ColumnConstraint/UniqueConstraintTest.cs
@@ -0,0 +1,27 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Statement.ColumnConstraint;
+
+namespace SQLite.CodeFirst.Test.Statement.ColumnConstraint
+{
+ [TestClass]
+ public class UniqueConstraintTest
+ {
+ [TestMethod]
+ public void CreateStatement_StatementIsCorrect_NoConstraint()
+ {
+ var uniqueConstraint = new UniqueConstraint();
+ uniqueConstraint.OnConflict = OnConflictAction.None;
+ string output = uniqueConstraint.CreateStatement();
+ Assert.AreEqual(output, "UNIQUE");
+ }
+
+ [TestMethod]
+ public void CreateStatement_StatementIsCorrect_WithConstraint()
+ {
+ var uniqueConstraint = new UniqueConstraint();
+ uniqueConstraint.OnConflict = OnConflictAction.Rollback;
+ string output = uniqueConstraint.CreateStatement();
+ Assert.AreEqual(output, "UNIQUE ON CONFLICT ROLLBACK");
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Attributes/OnConflictAction.cs b/SQLite.CodeFirst/Attributes/OnConflictAction.cs
new file mode 100644
index 0000000..e2df5b0
--- /dev/null
+++ b/SQLite.CodeFirst/Attributes/OnConflictAction.cs
@@ -0,0 +1,16 @@
+namespace SQLite.CodeFirst
+{
+ ///
+ /// The action to resolve a UNIQUE constraint violation.
+ /// Is used together with the .
+ ///
+ public enum OnConflictAction
+ {
+ None,
+ Rollback,
+ Abort,
+ Fail,
+ Ignore,
+ Replace
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Attributes/UniqueAttribute.cs b/SQLite.CodeFirst/Attributes/UniqueAttribute.cs
new file mode 100644
index 0000000..913dcde
--- /dev/null
+++ b/SQLite.CodeFirst/Attributes/UniqueAttribute.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace SQLite.CodeFirst
+{
+ ///
+ /// The UNIQUE Constraint prevents two records from having identical values in a particular column.
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public sealed class UniqueAttribute : Attribute
+ {
+ public UniqueAttribute(OnConflictAction onConflict = OnConflictAction.None)
+ {
+ OnConflict = onConflict;
+ }
+
+ public OnConflictAction OnConflict { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/DbInitializers/SqliteInitializerBase.cs b/SQLite.CodeFirst/DbInitializers/SqliteInitializerBase.cs
index b063412..eee4db8 100644
--- a/SQLite.CodeFirst/DbInitializers/SqliteInitializerBase.cs
+++ b/SQLite.CodeFirst/DbInitializers/SqliteInitializerBase.cs
@@ -3,6 +3,7 @@
using System.Data.Entity.ModelConfiguration.Conventions;
using SQLite.CodeFirst.Convention;
using System.IO;
+using System.Linq;
using SQLite.CodeFirst.Utility;
namespace SQLite.CodeFirst
@@ -36,6 +37,10 @@ protected SqliteInitializerBase(DbModelBuilder modelBuilder)
// See https://github.com/msallin/SQLiteCodeFirst/issues/7 for details.
modelBuilder.Conventions.Remove();
+ modelBuilder.Properties()
+ .Having(x => x.GetCustomAttributes(false).OfType().FirstOrDefault())
+ .Configure((config, attribute) => config.HasColumnAnnotation("IsUnique", attribute));
+
// By default there is a 'ForeignKeyIndexConvention' but it can be removed.
// And there is no "Contains" and no way to enumerate the ConventionsCollection.
// So a try/catch will do the job.
diff --git a/SQLite.CodeFirst/Internal/Builder/ColumnStatementCollectionBuilder.cs b/SQLite.CodeFirst/Internal/Builder/ColumnStatementCollectionBuilder.cs
index ca8d8b1..5a568b7 100644
--- a/SQLite.CodeFirst/Internal/Builder/ColumnStatementCollectionBuilder.cs
+++ b/SQLite.CodeFirst/Internal/Builder/ColumnStatementCollectionBuilder.cs
@@ -36,6 +36,7 @@ private IEnumerable CreateColumnStatements()
AddMaxLengthConstraintIfNecessary(property, columnStatement);
AdjustDatatypeForAutogenerationIfNecessary(property, columnStatement);
AddNullConstraintIfNecessary(property, columnStatement);
+ AddUniqueConstraintIfNecessary(property, columnStatement);
yield return columnStatement;
}
@@ -66,5 +67,19 @@ private static void AddNullConstraintIfNecessary(EdmProperty property, ColumnSta
columnStatement.ColumnConstraints.Add(new NotNullConstraint());
}
}
+
+ private static void AddUniqueConstraintIfNecessary(EdmProperty property, ColumnStatement columnStatement)
+ {
+ MetadataProperty item;
+ bool found = property.MetadataProperties.TryGetValue("http://schemas.microsoft.com/ado/2013/11/edm/customannotation:IsUnique", true, out item);
+ if (found)
+ {
+ var value = (UniqueAttribute)item.Value;
+ columnStatement.ColumnConstraints.Add(new UniqueConstraint
+ {
+ OnConflict = value.OnConflict
+ });
+ }
+ }
}
}
diff --git a/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/UniqueConstraint.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/UniqueConstraint.cs
new file mode 100644
index 0000000..7859686
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/UniqueConstraint.cs
@@ -0,0 +1,20 @@
+using System.Text;
+
+namespace SQLite.CodeFirst.Statement.ColumnConstraint
+{
+ internal class UniqueConstraint : IColumnConstraint
+ {
+ private const string Template = "UNIQUE {conflict-clause}";
+
+ public OnConflictAction OnConflict { get; set; }
+
+ public string CreateStatement()
+ {
+ var sb = new StringBuilder(Template);
+
+ sb.Replace("{conflict-clause}", OnConflict != OnConflictAction.None ? "ON CONFLICT " + OnConflict.ToString().ToUpperInvariant() : string.Empty);
+
+ return sb.ToString().Trim();
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/SQLite.CodeFirst.csproj b/SQLite.CodeFirst/SQLite.CodeFirst.csproj
index 1f1c951..5392970 100644
--- a/SQLite.CodeFirst/SQLite.CodeFirst.csproj
+++ b/SQLite.CodeFirst/SQLite.CodeFirst.csproj
@@ -83,10 +83,13 @@
Properties\AssemblySharedInfo.cs
+
+
+