diff --git a/FluentCassandra.Sandbox/Program.cs b/FluentCassandra.Sandbox/Program.cs index 25f941c..25c6dc5 100644 --- a/FluentCassandra.Sandbox/Program.cs +++ b/FluentCassandra.Sandbox/Program.cs @@ -8,75 +8,56 @@ namespace FluentCassandra.Sandbox { - internal class Program + public class Location { - private static void Main(string[] args) - { - dynamic location = new FluentColumnFamily { - Key = "19001", - ColumnFamily = "Location" - }; - - location.Latitude = 30.0M; - location.Longitude = -40.0M; - - location.Street = "123 Some St."; - location.City = "Philadelphia"; - location.State = "PA"; - location.PostalCode = "19001"; - - location.Name = "Some Location"; - - Console.WriteLine("----------------------"); - foreach (var col in location) - Console.WriteLine(col.ToString()); - - Console.WriteLine("----------------------"); - TTransport transport = new TSocket("localhost", 9160); - TProtocol protocol = new TBinaryProtocol(transport); - Cassandra.Client client = new Cassandra.Client(protocol); - - transport.Open(); + public string Id; - var utf8 = Encoding.UTF8; - string keySpace = "Keyspace1"; - string key = ((FluentColumnFamily)location).Key; - string columnFamily = ((FluentColumnFamily)location).ColumnFamily; + public decimal Latitude; + public decimal Longitude; + public string Street; + public string City; + public string State; + public string PostalCode; + public string Name; + } - foreach (var col in (FluentColumnFamily)location) - { - var path = new ColumnPath { - Column_family = columnFamily, - Column = col.NameBytes - }; + public class LocationMap : ColumnFamilyMap + { + public LocationMap() + { + Keyspace("Keyspace1"); + ColumnFamily("Location"); + Key(x => x.Id); + Map(x => x.Latitude); + Map(x => x.Longitude); + Map(x => x.Street); + Map(x => x.City); + Map(x => x.State); + Map(x => x.PostalCode); + Map(x => x.Name); + } + } - client.insert( - keySpace, - key, - path, - col.ValueBytes, - col.Timestamp.Ticks, - ConsistencyLevel.ONE - ); + internal class Program + { + private static void Main(string[] args) + { + var mapping = new LocationMap(); - Console.WriteLine("Inserted " + col.Name); + var location = new Location { + Id = "19001", + Name = "Some Location", - var column = client.get( - keySpace, - key, - path, - ConsistencyLevel.ONE - ); + Latitude = 30.0M, + Longitude = -40.0M, - Console.WriteLine( - "{0}: {1} - {2}", - utf8.GetString(column.Column.Name), - utf8.GetString(column.Column.Value), - column.Column.Timestamp - ); - } + Street = "123 Some St.", + City = "Philadelphia", + State = "PA", + PostalCode = "19001" + }; - transport.Close(); + mapping.Insert(location); Console.Read(); } diff --git a/FluentCassandra.suo b/FluentCassandra.suo index fac95fa..c19b756 100644 Binary files a/FluentCassandra.suo and b/FluentCassandra.suo differ diff --git a/FluentCassandra/ColumnFamilyMap.cs b/FluentCassandra/ColumnFamilyMap.cs new file mode 100644 index 0000000..6d0a0e5 --- /dev/null +++ b/FluentCassandra/ColumnFamilyMap.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Linq.Expressions; +using Thrift.Transport; +using Thrift.Protocol; +using Apache.Cassandra; + +namespace FluentCassandra +{ + public class ColumnFamilyMap + { + private string _keyspace; + private string _columnFamily; + private Func _key; + private IDictionary> _cache; + + public ColumnFamilyMap() + { + _cache = new Dictionary>(); + } + + protected void Keyspace(string keyspace) + { + _keyspace = keyspace; + } + + protected void ColumnFamily(string columnFamily) + { + _columnFamily = columnFamily; + } + + protected void Key(Expression> exp) + { + _key = exp.Compile(); + } + + protected void Map(Expression> exp) + { + Map(exp, null); + } + + protected void Map(Expression> exp, string columnName) + { + // if the column name isn't present then use the member reference name + if (String.IsNullOrWhiteSpace(columnName)) + { + columnName = GetName(exp); + + if (columnName == null) + throw new NotSupportedException("The expression could not be used to determine the column name automatically."); + } + + var func = exp.Compile(); + _cache.Add(columnName, func); + } + + private string GetName(Expression exp) + { + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + return ((MemberExpression)exp).Member.Name; + + case ExpressionType.Convert: + case ExpressionType.Quote: + return GetName(((UnaryExpression)exp).Operand); + + case ExpressionType.Lambda: + return GetName(((LambdaExpression)exp).Body); + + default: + return null; + } + } + + public FluentColumnFamily PrepareColumnFamily(T obj) + { + FluentColumnFamily record = new FluentColumnFamily(); + record.ColumnFamily = _columnFamily; + record.Key = _key(obj); + + foreach (var col in _cache) + { + record.Columns.Add(new FluentColumn { + Name = col.Key, + Value = col.Value(obj) + }); + } + + return record; + } + + public void Insert(T obj) + { + Insert(PrepareColumnFamily(obj)); + } + + public void Insert(FluentColumnFamily record) + { + TTransport transport = new TSocket("localhost", 9160); + TProtocol protocol = new TBinaryProtocol(transport); + Cassandra.Client client = new Cassandra.Client(protocol); + + transport.Open(); + + var utf8 = Encoding.UTF8; + string keySpace = _keyspace; + string columnFamily = record.ColumnFamily; + string key = record.Key; + + foreach (var col in record) + { + var path = new ColumnPath { + Column_family = columnFamily, + Column = col.NameBytes + }; + + client.insert( + keySpace, + key, + path, + col.ValueBytes, + col.Timestamp.Ticks, + ConsistencyLevel.ONE + ); + } + + transport.Close(); + } + + public void Remove(T obj) + { + RemoveByKey(_key(obj)); + } + + public void RemoveByKey(string key) + { + TTransport transport = new TSocket("localhost", 9160); + TProtocol protocol = new TBinaryProtocol(transport); + Cassandra.Client client = new Cassandra.Client(protocol); + + transport.Open(); + + var utf8 = Encoding.UTF8; + string keySpace = _keyspace; + string columnFamily = _columnFamily; + + var path = new ColumnPath { + Column_family = columnFamily + }; + + client.remove( + keySpace, + key, + path, + DateTimeOffset.UtcNow.Ticks, + ConsistencyLevel.ONE + ); + + transport.Close(); + } + + public void RemoveColumn(string key, string columnName) + { + TTransport transport = new TSocket("localhost", 9160); + TProtocol protocol = new TBinaryProtocol(transport); + Cassandra.Client client = new Cassandra.Client(protocol); + + transport.Open(); + + var utf8 = Encoding.UTF8; + string keySpace = _keyspace; + string columnFamily = _columnFamily; + + var path = new ColumnPath { + Column_family = columnFamily, + Column = FluentColumn.GetBytes(columnName) + }; + + client.remove( + keySpace, + key, + path, + DateTimeOffset.UtcNow.Ticks, + ConsistencyLevel.ONE + ); + + transport.Close(); + } + } +} \ No newline at end of file diff --git a/FluentCassandra/FluentCassandra.csproj b/FluentCassandra/FluentCassandra.csproj index 7377cfa..842c6fb 100644 --- a/FluentCassandra/FluentCassandra.csproj +++ b/FluentCassandra/FluentCassandra.csproj @@ -74,6 +74,7 @@ + diff --git a/FluentCassandra/FluentColumn.cs b/FluentCassandra/FluentColumn.cs index 0193858..c9f78cb 100644 --- a/FluentCassandra/FluentColumn.cs +++ b/FluentCassandra/FluentColumn.cs @@ -43,8 +43,11 @@ public byte[] ValueBytes get { return GetBytes(Value); } } - private byte[] GetBytes(object obj) + public static byte[] GetBytes(object obj) { + if (obj is Guid) + return ((Guid)obj).ToByteArray(); + switch (Type.GetTypeCode(obj.GetType())) { case TypeCode.Byte: @@ -73,7 +76,7 @@ private byte[] GetBytes(object obj) case TypeCode.UInt64: return BitConverter.GetBytes((ulong)obj); case TypeCode.Decimal: - return GetBytes((decimal)obj); + return FromDecimal((decimal)obj); case TypeCode.String: return Encoding.UTF8.GetBytes((string)obj); default: @@ -92,7 +95,7 @@ public static decimal ToDecimal(byte[] bytes) return new decimal(bits); } - public static byte[] GetBytes(decimal d) + public static byte[] FromDecimal(decimal d) { byte[] bytes = new byte[16];