0

我是 Marten 的新手,对于应该简单的查询感到非常困难。我有一个具有 EmailAddress 类型属性的 Person 类。我想找到任何具有特定电子邮件地址的人。

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 插入类型。序列化程序运行良好,能够使用它们的非公共设置器对这些类型进行序列化和反序列化。但是任何与 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);
        }
    }
4

1 回答 1

0

在我们的例子中,问题在于我们的集合属性没有被序列化为 JSON 数组。

我们通过将CollectionStorageJson 序列化器的属性 设置为 来解决这个问题CollectionStorage.AsArray,因此集合被序列化为 JSON 数组。

请参阅此链接中的“收集存储”部分:https ://martendb.io/documentation/documents/json/newtonsoft/

我们还需要设置TypeNameHandlingNone, 以防止在我们的集合被序列化并保存到数据库时存储类型元数据。(有关更多信息,请参阅上述链接的介绍部分。)

var serialiser = new JsonNetSerializer
{
    CollectionStorage = CollectionStorage.AsArray,
    EnumStorage = EnumStorage.AsString
};

serialiser.Customize(x =>
{
    x.TypeNameHandling = TypeNameHandling.None;
});
于 2021-07-20T12:54:13.500 回答