16

Windows Azure テーブル ストレージは、10 進データ型をサポートしていません。

推奨される回避策は、カスタム属性を使用して decimal プロパティを文字列としてシリアル化することです。

[EntityDataType(PrimitiveTypeKind.String)]
public decimal Quantity { get; set; }

この EntityDataType カスタム属性を実装して、10 進数のプロパティを格納し、Windows Azure テーブルから取得できるようにするにはどうすればよいでしょうか?

4

5 に答える 5

15

ReadEntityこれには、基本クラスでのオーバーライドWriteEntityが適しています。EntityResolverエンティティを取得するたびに を記述する必要はありません。

public class CustomTableEntity : TableEntity
{
    public override void ReadEntity(IDictionary<string, EntityProperty> properties, OperationContext operationContext)
    {
        base.ReadEntity(properties, operationContext);

        foreach (var thisProperty in
            GetType().GetProperties().Where(thisProperty =>
                thisProperty.GetType() != typeof(string) &&
                properties.ContainsKey(thisProperty.Name) &&
                properties[thisProperty.Name].PropertyType == EdmType.String))
        {
            var parse = thisProperty.PropertyType.GetMethods().SingleOrDefault(m =>
                m.Name == "Parse" &&
                m.GetParameters().Length == 1 &&
                m.GetParameters()[0].ParameterType == typeof(string));

            var value = parse != null ?
                parse.Invoke(thisProperty, new object[] { properties[thisProperty.Name].StringValue }) :
                Convert.ChangeType(properties[thisProperty.Name].PropertyAsObject, thisProperty.PropertyType);

            thisProperty.SetValue(this, value);
        }
    }

    public override IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext)
    {
        var properties = base.WriteEntity(operationContext);

        foreach (var thisProperty in
            GetType().GetProperties().Where(thisProperty =>
                !properties.ContainsKey(thisProperty.Name) &&
                typeof(TableEntity).GetProperties().All(p => p.Name != thisProperty.Name)))
        {
            var value = thisProperty.GetValue(this);
            if (value != null)
            {
                properties.Add(thisProperty.Name, new EntityProperty(value.ToString()));
            }
        }

        return properties;
    }
}

使用するときは、エンティティを拡張するだけで、エンティティCustomTableEntityを挿入または取得するときに透明になります。DateTimeTimeSpan、およびメソッドまたは実装インターフェイスdecimalを持つ型をサポートします。ParseIConvertible

于 2014-09-08T04:08:42.393 に答える
6

TableEntity の WriteEntity メソッドをオーバーライドし、EntityResolver を使用できます。

public class CustomTableEntity : TableEntity
{
    private const string DecimalPrefix = "D_";

    public override IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext)
    {
        var entityProperties = base.WriteEntity(operationContext);
        var objectProperties = GetType().GetProperties();

        foreach (var item in objectProperties.Where(f => f.PropertyType == typeof (decimal)))
        {
            entityProperties.Add(DecimalPrefix + item.Name, new EntityProperty(item.GetValue(this, null).ToString()));
        }

        return entityProperties;
    }
}

使用するエンティティ

public class MyEntity : CustomTableEntity
{
    public string MyProperty { get; set; }

    public decimal MyDecimalProperty1 { get; set; }
    public decimal MyDecimalProperty2 { get; set; }
}

テーブルの作成/挿入/取得を含む使用法

#region connection

CloudStorageAccount account = CloudStorageAccount.DevelopmentStorageAccount;
CloudTableClient client = account.CreateCloudTableClient();
CloudTable table = client.GetTableReference("mytable");
table.CreateIfNotExists();

#endregion


const string decimalPrefix = "D_";

const string partitionKey = "BlaBlaBla";
string rowKey = DateTime.Now.ToString("yyyyMMddHHmmss");


#region Insert

var entity = new MyEntity
    {
        PartitionKey = partitionKey,
        RowKey = rowKey,
        MyProperty = "Test",
        MyDecimalProperty1 = (decimal) 1.2,
        MyDecimalProperty2 = (decimal) 3.45
    };

TableOperation insertOperation = TableOperation.Insert(entity);
table.Execute(insertOperation);

#endregion



#region Retrieve

EntityResolver<MyEntity> myEntityResolver = (pk, rk, ts, props, etag) =>
    {
        var resolvedEntity = new MyEntity {PartitionKey = pk, RowKey = rk, Timestamp = ts, ETag = etag};

        foreach (var item in props.Where(p => p.Key.StartsWith(decimalPrefix)))
        {
            string realPropertyName = item.Key.Substring(decimalPrefix.Length);
            System.Reflection.PropertyInfo propertyInfo = resolvedEntity.GetType().GetProperty(realPropertyName);
            propertyInfo.SetValue(resolvedEntity, Convert.ChangeType(item.Value.StringValue, propertyInfo.PropertyType), null);

        }

        resolvedEntity.ReadEntity(props, null);

        return resolvedEntity;
    };

TableOperation retrieveOperation = TableOperation.Retrieve(partitionKey, rowKey, myEntityResolver);
TableResult retrievedResult = table.Execute(retrieveOperation);
var myRetrievedEntity = retrievedResult.Result as MyEntity;

// myRetrievedEntity.Dump(); 

#endregion
于 2013-04-20T02:38:08.513 に答える
2

Lokad.Cloud FatEntities製品を試してみましたか?

テーブルに保存したいオブジェクト全体にバイナリシリアライザーを使用しているだけだと思います。「Object-to-Cloud マッパー」プロジェクトも見てみる価値があるかもしれません。

https://github.com/Lokad/lokad-cloud

于 2012-06-17T19:57:33.230 に答える
0

プロパティのタイプを に変更できますdouble。次に、テーブル エンティティを独自のドメイン タイプにマッピングすることによって変換する必要がdecimalあります。もう 1 つのオプションは、1 つのフィールドdoubleに基づくエンティティに 2 つのプロパティを持たせることです。ただし、おそらくプロパティの名前をdecimal使用し続けたいと思うでしょう。これはテーブルに格納されるプロパティであるため、 とをオーバーライドして、このプロパティの名前を に変更する必要があります。次に、ここで提案されている他のソリューションのいくつかを使用することもできます。QuantitydecimaldoubleQuantityReadEntityWriteEntity

decimalここで、 a を aとして保存すると、double一部の値が正しくラウンドトリップしないと考えるかもしれません。2 つのタイプの範囲と精度が非常に異なるという理由だけで往復しない値は確かにありますが、天文学的に大きくなく、人間が読める精度を持つ金銭的な値のようなほとんどの「通常の」値は問題なく往復します。doubleこれは、からdecimalas によって実行されるへの変換Convert.ToDoubleには特別なプロパティがあるためです。

このメソッドによって返される Decimal 値には、最大 15 桁の有効数字が含まれます。

これにより、問題のある数値のラウンドトリップがどのように機能するかの例を次に示します。

var originalValue = 2.24M;
var doubleValue = (double) originalValue;

問題は、10 進数を使用した有理数 1/3 の正確な表現がないのと同じように、浮動小数点を使用した 10 進数 2.24 の正確な表現がないことです (2.24 は有理数 224/100 です)。0.3333333333333333 は 1/3 と同じではありません。doubleValueこれは、十分な精度で印刷することで確認できます。Console.WriteLine($"{doubleValue:G17}")収量

2.2400000000000002

ただし、値のラウンドトリップは引き続き機能します。

var roundTripValue = (decimal) doubleValue;

Console.WriteLine(roundTripValue)は利回り

2.24

したがって、値に対して計算を行わない限り、との間の変換が上記の .NET 規則に準拠していれば、doubleそれらを使用して値を格納できます。decimaldoubledecimal

于 2018-05-12T13:42:19.327 に答える