Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjustments to LINQ queries against duplicated fields. Closes GH-3150… #3153

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/documents/querying/linq/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ implements the traditional [IQueryable](https://msdn.microsoft.com/en-us/library
/// <returns></returns>
IMartenQueryable<T> Query<T>();
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten/IQuerySession.cs#L137-L147' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_querying_with_linq' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten/IQuerySession.cs#L142-L152' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_querying_with_linq' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

To query for all documents of a type - not that you would do this very often outside of testing - use the `Query<T>()` method like this:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Marten;
using Marten.Schema;
using Marten.Testing.Harness;
using Shouldly;
using Xunit.Abstractions;

namespace LinqTests.Bugs;

public class Bug_3151_using_duplicated_fields_in_linq_queries : BugIntegrationContext
{
private readonly ITestOutputHelper _output;

public Bug_3151_using_duplicated_fields_in_linq_queries(ITestOutputHelper output)
{
_output = output;
}


[Fact]
public async Task filtering_by_null_ids_works()
{
var p1 = new Person { ChildId = Guid.NewGuid()};
var p2 = new Person { ChildId = Guid.NewGuid()};
theSession.Store(p1,p2);
await theSession.SaveChangesAsync();
Guid?[] ids = [p1.ChildId];
var results = await theSession.Query<Person>()
.Where(x => x.ChildId.In(ids))
.ToListAsync();
results.Single().ChildId.ShouldBe(p1.ChildId);
}

[Fact]
public async Task filtering_by_non_null_ids_using_value_fails_regression_from_641()
{
var p1 = new Person { ChildId = Guid.NewGuid()};
var p2 = new Person { ChildId = Guid.NewGuid()};
theSession.Store(p1,p2);
await theSession.SaveChangesAsync();
Guid[] ids = [p1.ChildId.Value];
var results = await theSession.Query<Person>()
.Where(x => x.ChildId!.Value.In(ids))
.ToListAsync();
results.Single().ChildId.ShouldBe(p1.ChildId);
}

[Fact]
public async Task Bug_3150_query_multiple_times()
{
// if you call BeginTransactionAsync then the issue goes away..
//await theSession.BeginTransactionAsync(CancellationToken.None);

async Task DoQuery()
{
var children = new Dictionary<Guid, Person3150>();
await theSession.Query<Person3150>()
.Include(x => x.ChildId!, children)
.ToListAsync();
}

theSession.Logger = new TestOutputMartenLogger(_output);

await DoQuery();

// second invocation throws MartenCommandException "42P01: relation "mt_temp_id_list1" does not exist"
await DoQuery();
}
}

public class Person
{
public Guid Id { get; set; }

[DuplicateField]
public Guid? ChildId { get; set; }
}

public class Person3150
{
public Guid Id { get; set; }

[DuplicateField]
public string? Name { get; set; }
public Guid? ChildId { get; set; }
}
37 changes: 22 additions & 15 deletions src/Marten/Internal/Storage/DocumentStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public abstract class DocumentStorage<T, TId>: IDocumentStorage<T, TId>, IHaveMe
private readonly string[] _selectFields;
private ISqlFragment? _defaultWhere;
protected Action<T, TId> _setter;
private readonly DocumentMapping _document;

public DocumentStorage(StorageStyle storageStyle, DocumentMapping document)
{
Expand All @@ -65,20 +66,7 @@ public DocumentStorage(StorageStyle storageStyle, DocumentMapping document)
var fieldSelector = _selectFields.Join(", ");
_selectClause = $"select {fieldSelector} from {document.TableName.QualifiedName} as d";


if (DuplicatedFields.Any())
{
var duplicatedFields = DuplicatedFields.Select(x => "d." + x.ColumnName).Where(x => !_selectFields.Contains(x));
var allFields = _selectFields.Concat(duplicatedFields).ToArray();
SelectClauseWithDuplicatedFields = new DuplicatedFieldSelectClause(TableName.QualifiedName, $"select {allFields.Join(", ")} from {document.TableName.QualifiedName} as d",
allFields, typeof(T), this);
}
else
{
SelectClauseWithDuplicatedFields = this;
}


_document = document;

_loaderSql =
$"select {fieldSelector} from {document.TableName.QualifiedName} as d where id = $1";
Expand Down Expand Up @@ -107,7 +95,26 @@ public DocumentStorage(StorageStyle storageStyle, DocumentMapping document)

public bool UseNumericRevisions { get; }

public ISelectClause SelectClauseWithDuplicatedFields { get; }
// TODO -- convert to a method in V8
// this has to be a new instance every time because of how it gets the FromObject
// renamed in Include() batches
public ISelectClause SelectClauseWithDuplicatedFields
{
get
{
if (DuplicatedFields.Any())
{
var duplicatedFields = DuplicatedFields.Select(x => "d." + x.ColumnName).Where(x => !_selectFields.Contains(x));
var allFields = _selectFields.Concat(duplicatedFields).ToArray();
return new DuplicatedFieldSelectClause(TableName.QualifiedName, $"select {allFields.Join(", ")} from {_document.TableName.QualifiedName} as d",
allFields, typeof(T), this);
}
else
{
return this;
}
}
}

MetadataColumn[] IHaveMetadataColumns.MetadataColumns()
{
Expand Down
2 changes: 2 additions & 0 deletions src/Marten/Linq/Members/DuplicatedField.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ public virtual TableColumn ToColumn()

public IQueryableMember FindMember(MemberInfo member)
{
if (member.Name == "Value") return this;

// Only really using this for string ToLower() and ToUpper()
if (MemberType == typeof(string))
{
Expand Down
Loading