[英]How can I make my class act like a Guid when used in Linq?
我将Cosmosdb与.net CORE 2.2和Cosmosdb SQL SDK结合使用。 默认情况下,cosmosdb将为每个文档分配“ Id”属性作为Guid。 但是仅凭ID不足以直接读取文档,还必须知道其分区。 因此,我创建了一个名为CosmosGuid的类,其中包含一个Id属性(Guid)和一个PartitionKey属性(字符串)。 ToString()被重写以在Guid上调用.ToString(“ N”)来删除破折号,并将PartitionKey附加到末尾。 我遇到的问题是,当我在Linq中使用CosmosGuid时,生成的SQL将包含CosmosGuid的json版本,我真的需要它只是一个字符串。 我可以调用.ToString(),这将产生所需的结果,但是恐怕其他开发人员会在Linq表达式中使用我的类,并且由于未知原因它会失败。 当我保存CosmosGuid时,我创建了一个自定义的newtonsoft转换器,以在保存时调用ToString()并在读取时调用.Parse(string)。 当您在Linq中比较两个Guid时,生成的SQL出现在一个字符串中,但是当我比较两个CosmosGuid时,它会创建我的类的json字符串。 我怎样才能使我的班级表现得像个向导?
我已经尝试实现与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版本。 (该ID也是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属性,则Cosmosdb SDK生成的SQL可以正常工作。 在Linq中使用时,如何使我的班级像.net Guid一样工作?
对于LINQ to对象:
您可以在CosmosGuid
类上重载==
运算符,请参见operator keyword 。
另外,您可以实现IEquatable<Guid>
并改用.Equals()
:
public class CosmosGuid : IEquatable<Guid>
{
....
public bool Equals(Guid other) {
return this.Guid == other;
}
}
.Where(x => cosmosGuid.Equals(x.Id))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.