Как я могу заставить свой класс вести себя как Guid при использовании в Linq? - PullRequest
1 голос
/ 14 мая 2019

Я использую Cosmosdb с .net CORE 2.2 и Cosmosdb SQL SDK.По умолчанию cosmosdb назначает каждому документу свойство Id в качестве Guid.Но одного только идентификатора недостаточно для непосредственного чтения документа, вы также должны знать его раздел.Поэтому я создал класс с именем CosmosGuid, который содержит свойство Id (Guid) и свойство PartitionKey (строка).ToString () переопределяется для вызова .ToString ("N") в Guid для удаления штрихов, и он добавляет PartitionKey в конец.У меня проблема в том, что когда я использую CosmosGuid в Linq, сгенерированный SQL будет содержать json-версию CosmosGuid, мне действительно нужно, чтобы она была просто строкой.Я могу вызвать .ToString (), и это приведет к желаемому результату, но я боюсь, что другой разработчик будет использовать мой класс в выражении Linq, и он потерпит неудачу по неизвестной причине.Когда я сохраняю CosmosGuid, я создал собственный конвертер newtonsoft для вызова ToString () при его сохранении и вызова .Parse (string) при чтении.Когда вы сравниваете два Guid в Linq, сгенерированный SQL получается в виде строки, но когда я сравниваю два CosmosGuid, он создает строку json моего класса.Как я могу заставить свой класс вести себя как Guid?

Я уже пытался реализовать все те же интерфейсы, что и Guid.Закрытие, которое я получил, реализовывало IEnumerable, и в GetComparer я возвратил:

new string[] { this.ToString() }.GetEnumerator();

Полученный код был идеальным, но он продолжал помещать мою строку в квадратные скобки [].

вот пример:

SELECT VALUE root FROM root WHERE (root['id'] = ['9a9dbbd5f78143c48b16f780c7ceaa4011'])

Это класс CosmosGuid, я полагаю, что id публикует полный класс, так как он не очень большой и может быть полезен для некоторых.

    public class CosmosGuid
    {
        // This is the unique Id of the entity
        public Guid Guid { get; set; }
        // This is the partition key where the entity lives
        public string PartitionKey { get; set; }
        // This is the unique Id of the Document that contains the entity
        public Guid? ParentGuid { get; set; }
        // This is the PartitionKey of the Document that contains the entity
        public string ParentPartitionKey { get; set; }

        /// <summary>
        /// Parses a CosmosGuid string into a new CosmosGuid
        /// </summary>
        /// <param name="cosmosGuid"></param>
        public CosmosGuid(string cosmosGuid)
        {
            ParentGuid = null;
            ParentPartitionKey = null;

            try
            {
                var parsed = cosmosGuid.Split('-');

                // We can accuratly parse the guid from the string by always grabing the first 32 characters.
                // The characters after the first 32 are the PartitionKey.
                // https://stackoverflow.com/a/4458925
                // Guid.NewGuid().ToString("N") => 32 characters (digits only, no dashes)

                Guid = Guid.Parse(parsed[0].Substring(0, 32));
                PartitionKey = parsed[0].Substring(32, parsed[0].Length - 32);

                if (parsed.Length == 2)
                {
                    ParentGuid = Guid.Parse(parsed[1].Substring(0, 32));
                    ParentPartitionKey = parsed[1].Substring(32, parsed[1].Length - 32);
                }
            }
            catch (Exception ex)
            {
                throw new Exception("The Id of the document is not a properly formatted CosmosGuid.", ex);
            }
        }

        /// <summary>
        /// Generates a new Guid and appends the PartitionKey. This is used for Documents.
        /// </summary>
        /// <param name="partitionKey"></param>
        /// <returns></returns>
        public static CosmosGuid NewCosmosGuid(string partitionKey)
        {
            return new CosmosGuid($"{ShortenGuid(Guid.NewGuid())}{partitionKey}");
        }

        /// <summary>
        /// Generates a new Guid and appends the PartitionKey as well as the Parent Guid and Parent PartitionKey. This is used for Subdocuments.
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="partitionKey"></param>
        /// <returns></returns>
        public static CosmosGuid NewCosmosGuid(CosmosGuid parent, string partitionKey)
        {
            return new CosmosGuid($"{ShortenGuid(Guid.NewGuid())}{partitionKey}-{ShortenGuid(parent.Guid)}{parent.PartitionKey}");
        }

        /// <summary>
        /// Returns only the Parent CosmosGuid. If there is no parent the value returned will be null.
        /// </summary>
        public CosmosGuid Parent
        {
            get
            {
                if (ParentGuid != null && ParentPartitionKey != null)
                    return new CosmosGuid($"{ShortenGuid((Guid)ParentGuid)}{ParentPartitionKey}");
                else
                    return null;
            }
        }

        /// <summary>
        /// Parses a CosmosGuid string into a new CosmosGuid.
        /// </summary>
        /// <param name="cosmosGuid"></param>
        /// <returns></returns>
        public static CosmosGuid Parse(string cosmosGuid)
        {
            return new CosmosGuid(cosmosGuid);
        }

        /// <summary>
        /// Generates a CosmosGuid formatted string.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            if (ParentGuid == null)
                return $"{ShortenGuid(Guid)}{PartitionKey}";
            else
                return $"{ShortenGuid(Guid)}{PartitionKey}-{ShortenGuid((Guid)ParentGuid)}{ParentPartitionKey}";
        }

        /// <summary>
        /// Removes the dashes from a Guid
        /// </summary>
        /// <param name="guid"></param>
        /// <returns></returns>
        private static string ShortenGuid(Guid guid)
        {
            // Just remove dashes from the guid to shorten it some.
            // More can be done here if you wish but make sure the guid uniqueness isnt compromised.
            return guid.ToString("N");
        }

        public static bool operator ==(CosmosGuid obj1, CosmosGuid obj2)
        {
            return obj1?.ToString() == obj2?.ToString();
        }

        public static bool operator !=(CosmosGuid obj1, CosmosGuid obj2)
        {
            return obj1?.ToString() != obj2?.ToString();
        }
    }

Если разработчик, где использовать CosmosGuid, не сможет работать, потому что сгенерированный SQL является версией Json класса.(идентификатор также является CosmosGuid):

var cosmosGuid = CosmosGuid.Parse("6bec688a0aca477c8175c09162b7a9b411");
var result = await Client.CreateDocumentQuery<MyClass>(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), options)
                                     .Where(x => x.Id == cosmosGuid)
                                     .AsDocumentQuery();

Это сгенерированный sql

SELECT VALUE root FROM root WHERE (root['id'] = {'Guid':'6bec688a-0aca-477c-8175-c09162b7a9b4','PartitionKey':'11','ParentGuid':null,'ParentPartitionKey':null,'Parent':null})

Вместо этого разработчик должен вызывать .ToString () везде в коде.

var cosmosGuid = CosmosGuid.Parse("6bec688a0aca477c8175c09162b7a9b411");
var result = await Client.CreateDocumentQuery<MyClass>(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), options)
                                     .Where(x => x.Id.ToString() == cosmosGuid.ToString())
                                     .AsDocumentQuery();

Это сгенерированный Sql

SELECT VALUE root FROM root WHERE (root['id'] = '6bec688a0aca477c8175c09162b7a9b411')

Если я удаляю CosmosGuid и возвращаюсь к использованию только идентификатора Guid в качестве свойства Id, SQL, сгенерированный Cosmosdb SDK, работает нормально.Как я могу заставить свой класс вести себя как .net Guid при использовании в Linq?

1 Ответ

0 голосов
/ 14 мая 2019

Для LINQ к объектам:

Вы можете перегрузить оператор == в своем классе CosmosGuid, см. ключевое слово оператора .

Кроме того, вы можете реализовать IEquatable<Guid> и использовать вместо него .Equals():

public class CosmosGuid : IEquatable<Guid>
{

  ....

  public bool Equals(Guid other) {
    return this.Guid == other;
  }
}
.Where(x => cosmosGuid.Equals(x.Id))
...