dtretyakov / WindowsAzure

.NET library aimed at managing and querying entities from Windows Azure Storage. It can be used as LINQ to Azure Tables.
MIT License
64 stars 27 forks source link

PartitionKey and RowKey can't be same property #39

Closed dvdstelt closed 9 years ago

dvdstelt commented 9 years ago

It's not unusual for us to have the same value for PartitionKey and RowKey. In our EntityMap we provide the same property for ParititionKey and RowKey. This throws an exception.

dtretyakov commented 9 years ago

Could you briefly describe what would you plan to reach when using same property as PK & RK?

dvdstelt commented 9 years ago

We have a lot of unique entities, which each have just a single row in TableStorage. Instead of relying on a fixed partitionkey like "AllCompanies" or something and have thousands and thousands of rows in a single partition, we'd like Azure to possibly partition all keys over unique partitions.

We just specify a single property in our model and specify it as both PartitionKey and RowKey.

dtretyakov commented 9 years ago

Ok, in this case just use a PartitionKeyAttribute without RowKeyAttribute.

dvdstelt commented 9 years ago

This also works with EntityTypeMap ?

tdietrich513 commented 9 years ago

No, EntityTypeMap seems to throw an exception when either PartitionKey or RowKey is not set.

Setup:

public class KeyOnly
{
    public string Key { get; set; }
    public string Data { get; set; }
}

public class KeyOnlyMap : EntityTypeMap<KeyOnly>
{
    public KeyOnlyMap()
    {
        PartitionKey(o => o.Key);
    }
}

Test:

[TestFixture]
public class ASETests
{
    [Test]
    public void CanUseKeyOnly()
    {
        EntityTypeMap.RegisterAssembly(GetType().Assembly);
        var storageAccount = CloudStorageAccount.DevelopmentStorageAccount;
        var tableClient = storageAccount.CreateCloudTableClient();
        var keyOnlyTable = new TableSet<KeyOnly>(tableClient);

        keyOnlyTable.CreateIfNotExists();

        var newEntity = new KeyOnly()
        {
            Key = "1",
            Data = "SomeData"
        };

        keyOnlyTable.Add(newEntity);
    }
}

Error: System.InvalidOperationException : PartitionKey or RowKey attribute should be defined for type 'AzureStorageExtensionsTesting.KeyOnly'.

The issue seems to be from this method in the EntityTypeMap class:

    private void CheckEntityMap()
    {
        // Check whether entity's composite key completely defined
        if (!_nameChanges.ContainsKey(PartitionKeyPropertyName) && !_nameChanges.ContainsValue(RowKeyPropertyName))
        {
            string message = string.Format(Resources.EntityTypeDataMissingKey, _entityType);
            throw new InvalidOperationException(message);
        }
    }

Which I'm not really understanding entirely (shouldn't it be ContainsValue in both cases? and || rather than &&?), but this seems to be by design, as there is a unit test to make sure an InvalidOperation is thrown for entities without a composite key:

    [Fact]
    public void CreateEntityTypeDataWithoutCompositeKey()
    {
        // Arrange
        EntityTypeData<EntityWithoutCompositeKey> typeData = null;

        // Act
        Assert.Throws<InvalidOperationException>(() => { typeData = new EntityTypeData<EntityWithoutCompositeKey>(); });

        // Assert
        Assert.Null(typeData);
    }

However you can just the attribute based mapping without a RowKey and it works just fine.

dtretyakov commented 9 years ago

But actually it's a bug which should be fixed.

dtretyakov commented 9 years ago

It was fixed in version 1.0.2