Я запускаю тест интеграции БД, который записывает, а затем читает запись рецепта из таблицы данных PostgreSQL 10. Я получаю сообщение об ошибке:
System.InvalidOperationException: конструктор по умолчанию без параметров или одна совпадающая подпись (System.Guid globalid, имя System.String, ингредиенты System.Array, инструкции System.Array, System. Для материализации Recipes.Recipe требуется источник строки, персонализированный System.Int32, заметки System.Array, теги System.Array *
Если я добавлю конструктор без параметров (publi c, приватный или внутренний) ), тест проходит. Я, конечно, могу это сделать (это закомментировано в приведенном ниже коде), но эта проблема озадачивает меня, потому что, насколько я могу судить, у меня есть конструктор, соответствующий указанной подписи.
Я пробовал несколько вещей:
Изменение элементов подписи с IEnumerable на string []
Изменение свойств IReadOnlyCollection на свойства List.
Изменение PeopleServed от int? int.
Изменение регистра аргументов в соответствии с нижним регистром, указанным в сообщении об ошибке.
Публикация конструктора c.
Предоставление коллекции IReadOnlyCollection. Свойства внутренних сеттеров.
Добавление Newtonsoft и добавление атрибута [JsonConstructor].
Изменение порядка появления конструкторов.
Понижение версии Dapper.
Что мне не хватает?
Таблица выглядит так:
Column | Type | Collation | Nullable | Default
---------------+-----------+-----------+----------+--------------------------------------------------------------
global_id | uuid | | not null |
name | text | | not null |
source | text | | |
ingredients | text[] | | not null |
instructions | text[] | | not null |
notes | text[] | | |
people_served | integer | | |
tags | text[] | | |
sys_period | tstzrange | | not null | tstzrange(CURRENT_TIMESTAMP, NULL::timestamp with time zone)
Indexes:
"recipes_pkey" PRIMARY KEY, btree (global_id)
SQL для создания таблицы:
CREATE TABLE IF NOT EXISTS public.recipes
(
global_id UUID NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
source TEXT NULL,
ingredients TEXT[] NOT NULL,
instructions TEXT[] NOT NULL,
notes TEXT[] NULL,
people_served INT NULL,
tags TEXT[] NULL,
sys_period tstzrange NOT NULL DEFAULT tstzrange(current_timestamp, null)
);
My SQL оператор :
SELECT
global_id AS GlobalId,
name,
ingredients,
instructions,
source,
people_served AS PeopleServed,
notes,
tags
FROM public.recipes
Мой класс рецептов:
public class Recipe : EntityBase, IEquatable<Recipe>
{
public Recipe(string name,
IEnumerable<string> ingredients,
IEnumerable<string> instructions,
string source = default,
int? peopleServed = default,
IEnumerable<string> notes = default,
IEnumerable<string> tags = default) : this(globalId: Guid.NewGuid(),
name: name,
source: source,
peopleServed: peopleServed,
ingredients: ingredients,
instructions: instructions,
notes: notes,
tags: tags)
{ }
// internal Recipe() { }
internal Recipe(Guid globalId,
string name,
IEnumerable<string> ingredients,
IEnumerable<string> instructions,
string source = default,
int? peopleServed = default,
IEnumerable<string> notes = default,
IEnumerable<string> tags = default) : base(globalId: globalId)
{
Name = string.IsNullOrWhiteSpace(name) ? throw new ArgumentNullException(nameof(name)) : name;
Ingredients = (ingredients?.Any() ?? false) ? new ReadOnlyCollection<string>(ingredients.ToList()) : throw new ArgumentNullException(nameof(ingredients));
Instructions = (instructions?.Any() ?? false) ? new ReadOnlyCollection<string>(instructions.ToList()) : throw new ArgumentNullException(nameof(instructions));
Notes = new ReadOnlyCollection<string>(notes?.ToList() ?? new List<string>());
Tags = new ReadOnlyCollection<string>(tags?.ToList() ?? new List<string>());
Source = source;
PeopleServed = peopleServed;
}
public string Name { get; internal set; }
public string Source { get; internal set; }
public int? PeopleServed { get; internal set; }
public IReadOnlyCollection<string> Ingredients { get; }
public IReadOnlyCollection<string> Instructions { get; }
public IReadOnlyCollection<string> Notes { get; }
public IReadOnlyCollection<string> Tags { get; }
public override bool Equals(object obj)
{
return Equals(obj as Recipe);
}
public bool Equals(Recipe other)
{
return other != null &&
GlobalId.Equals(other.GlobalId) &&
Name == other.Name &&
Source == other.Source &&
PeopleServed == other.PeopleServed &&
Ingredients.SequenceEqual(other.Ingredients) &&
Instructions.SequenceEqual(other.Instructions) &&
Notes.SequenceEqual(other.Notes) &&
Tags.SequenceEqual(other.Tags);
}
public override int GetHashCode()
{
var hashCode = 1374191493;
hashCode = hashCode * -1521134295 + GlobalId.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Name);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Source);
hashCode = hashCode * -1521134295 + PeopleServed.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<IReadOnlyCollection<string>>.Default.GetHashCode(Ingredients);
hashCode = hashCode * -1521134295 + EqualityComparer<IReadOnlyCollection<string>>.Default.GetHashCode(Instructions);
hashCode = hashCode * -1521134295 + EqualityComparer<IReadOnlyCollection<string>>.Default.GetHashCode(Notes);
hashCode = hashCode * -1521134295 + EqualityComparer<IReadOnlyCollection<string>>.Default.GetHashCode(Tags);
return hashCode;
}
public override string ToString()
{
return Name;
}
}
Базовый класс:
public abstract class EntityBase : IEquatable<EntityBase>
{
public Guid GlobalId { get; protected set; }
protected EntityBase() : this(Guid.NewGuid()) { }
protected EntityBase(Guid globalId)
{
if (globalId.Equals(Guid.Empty)) { throw new ArgumentException($"{nameof(globalId)} cannot be an empty Guid."); }
GlobalId = globalId;
}
public override string ToString()
{
return GlobalId.ToString();
}
public override bool Equals(object obj)
{
return Equals(obj as EntityBase);
}
public bool Equals(EntityBase other)
{
return other != null &&
GlobalId.Equals(other.GlobalId);
}
public override int GetHashCode()
{
return -2093202133 + GlobalId.GetHashCode();
}
}
Неудачный тест (который проходит с конструктором по умолчанию):
[Fact]
public async Task InsertAndFetchRecipeAsync()
{
var recipe = new Recipe(name: "Test",
ingredients: new List<string>() { "cucumber", "tomato" },
instructions: new List<string>() { "Chop up", "Mix together", "Season" },
source: nameof(InsertAndFetchRecipeAsync),
peopleServed: 1,
notes: new List<string>() { "Test recipe", "Yummy" },
tags: new List<string>() { "vegan", "WFPB" });
var recipes = new Recipes(fixture.ConnectionString);
await recipes.SaveRecipeAsync(recipe).ConfigureAwait(false);
var fromDb = await recipes.GetRecipeAsync(recipe.GlobalId).ConfigureAwait(false);
Assert.Equal(recipe, fromDb);
recipe = new Recipe(name: "Test",
ingredients: new List<string>() { "cucumber", "tomato" },
instructions: new List<string>() { "Chop up", "Mix together", "Season" },
source: null,
peopleServed: null,
notes: null,
tags: null);
await recipes.SaveRecipeAsync(recipe).ConfigureAwait(false);
fromDb = await recipes.GetRecipeAsync(recipe.GlobalId).ConfigureAwait(false);
Assert.Equal(recipe, fromDb);
}
Кто-нибудь? Что тут происходит? Помощь всегда ценится.