如何解决无法使用 marten 和 Linq 查询子类型
我是 marten 的新手,对于应该很简单的查询遇到了严重的困难。我有一个 Person 类,它有一个类型为 EmailAddress 的属性。我想找到任何拥有特定电子邮件地址的人。
public class EmailAddress : ValueObject
{
// construction
protected EmailAddress() { } // needed for deserialization
public EmailAddress(EmailType type,string emailAddress)
{
Guard.Against.Null(type,nameof(type));
Guard.Against.NullOrWhiteSpace(emailAddress,nameof(emailAddress));
Guard.Against.NotValidEmail(emailAddress,nameof(emailAddress));
if (!(type.HasFlag(EmailType.Personal) || type.HasFlag(EmailType.Work) || type.HasFlag(EmailType.Other))) throw new ArgumentException("Unable to craete an EmailAddress without a valid type (i.e.,Personal,Work,Other).");
Type = type;
Address = emailAddress;
}
// properties
public EmailType Type { get; private set; }
public string Address { get; private set; }
}
public class Person : ValueObject
{
// fields
List<EmailAddress> _emails = new List<EmailAddress>();
// Construction
protected Person() { } // needed for deserialization
public Person(Guid id,string firstName,string lastName,EmailAddress identityAddress)
{
Guard.Against.Default(id,nameof(id));
Guard.Against.NullOrWhiteSpace(firstName,nameof(firstName));
Guard.Against.NullOrWhiteSpace(lastName,nameof(lastName));
Guard.Against.Null(identityAddress,nameof(identityAddress));
if (!identityAddress.Type.HasFlag(EmailType.IsIdentity))
{
identityAddress = new EmailAddress(identityAddress.Type | EmailType.IsIdentity,identityAddress.Address);
}
this.Id = id;
this.FirstName = firstName;
this.LastName = lastName;
_emails.Add(identityAddress);
}
// properties
public Guid Id { get; private set; }
public string FirstName { get; private set; }
public string LastName { get; private set; }
public IReadOnlyList<EmailAddress> Emails { get => _emails; }
}
我创建了一个连接到 Postgresql 的测试类,并且能够使用 marten 插入类型。序列化器运行良好,能够使用它们的非公共 setter 序列化和反序列化这些类型。但是任何与 Person 类型的 Emails 属性相关的查询都会失败。
[TestMethod]
public void Can_Find_Person_Manual()
{
// set the test values
Guid lukeId = Guid.NewGuid();
EmailAddress lukeEmail = new EmailAddress(EmailType.Work | EmailType.IsIdentity,"luke@skywalkerranch.com");
Person lukeSkywalker = new Person(lukeId,"Luke","Skywalker",lukeEmail);
Guid leiaId = Guid.NewGuid();
EmailAddress leiaEmail = new EmailAddress(EmailType.Personal | EmailType.IsIdentity,"leia@skywalkerranch.com");
Person leiaSolo = new Person(leiaId,"Leia","Solo",leiaEmail);
// add a test object
IDocumentStore database = _container.GetInstance<IDocumentStore>();
using (var session = database.LightweightSession())
{
// clear prior people
session.DeleteWhere<Person>(p => true);
session.SaveChanges();
// Add new people
session.Store<Person>(lukeSkywalker);
session.Store<Person>(leiaSolo);
session.SaveChanges();
// Start Testing
IReadOnlyList<Person> people = session.LoadMany<Person>(new Guid[] { lukeId,leiaId });
Assert.IsTrue(people.Count == 2);
Person luke = session.Load<Person>(lukeId);
Assert.IsTrue(luke.Id == lukeId);
Assert.IsTrue(luke.Emails.Contains(e => e.Address == "luke@skywalkerranch.com"));
Person foundLuke = session.Query<Person>().Where(p => p.FirstName == "Luke").First();
Assert.IsTrue(foundLuke.Id == lukeId);
Person leia = session.Load<Person>(leiaId);
Assert.IsTrue(leia.Id == leiaId);
Assert.IsTrue(leia.Emails.Contains(e => e.Address == "leia@skywalkerranch.com"));
List<Person> allPeople = session.Query<Person>().ToList(); // works fine 2 items
List<Person> allLeias = session.Query<Person>().Where(p => p.FirstName == "Leia").ToList(); // works fine 1 item
List<Person> allBills = session.Query<Person>().Where(p => p.FirstName == "Bill").ToList(); // works fine 0 items
// List<Person> withEmail = session.Query<Person>().Where(p => p.Emails.Count > 0).ToList(); // ERROR returns empty list
// List<Person> withEmail = session.Query<Person>().Where(p => p.Emails.Count(e => true) > 0).ToList(); // ERROR: select d.data,d.id,d.mt_version from public.mt_doc_person as d where jsonb_array_length(CAST(d.data ->> 'Emails' as jsonb)) > :arg0$ $ 22023: cannot get array length of a non - array'
List<Person> allLukes = session.Query<Person>().Where(p => p.Emails.Any(e => e.Address == "luke@skywalkerranch.com")).ToList(); // ERROR NullreferenceException
//// should get Leia
List<Person> withPersonalEmail = session.Query<Person>().Where(p => p.Emails.Any(e => e.Type == EmailType.Personal)).ToList(); // ERROR returns empty List
List<Person> ranchEmail = session.Query<Person>().Where(p => p.Emails.Any(e => e.Address.Contains("ranch"))).ToList(); // ERROR 'Specific method not supported'
// Below is the one is need
Person foundLeia = session.Query<Person>().Where(p => p.Emails.Any(_ => _.Address == "leia@skywalkerranch.com")).SingleOrDefault(); // ERROR returns null
Assert.IsTrue(foundLeia.Id == leiaId);
}
}
解决方法
在我们的例子中,问题是我们的集合属性没有被序列化为 JSON 数组。
我们通过将 Json 序列化器的 CollectionStorage
属性设置为
CollectionStorage.AsArray
,因此集合被序列化为 JSON 数组。
请参阅以下链接中的“集合存储”部分:https://martendb.io/documentation/documents/json/newtonsoft/
我们还需要将 TypeNameHandling
设置为 None
,以防止在我们的集合被序列化并保存到数据库时存储类型元数据。 (有关详细信息,请参阅上述链接的介绍部分。)
var serialiser = new JsonNetSerializer
{
CollectionStorage = CollectionStorage.AsArray,EnumStorage = EnumStorage.AsString
};
serialiser.Customize(x =>
{
x.TypeNameHandling = TypeNameHandling.None;
});
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。