LINQ 2 DB вставить динамический в таблицу

#.net #orm #repository #linq2db

#.net #орм #репозиторий #linq2db #orm

Вопрос:

Я хочу использовать dynamic, чтобы иметь возможность выбирать, какие столбцы определять, и те, которые я хочу разрешить базе данных устанавливать DEFAULT значение. Есть ли способ вставить dynamic в таблицу и вернуть Id ?

Я пробовал различные перегрузки как на DataConnection , так и ITable<> , но единственный результат, которого я смог добиться, — это получить количество затронутых строк.

Это мой код:

Схема таблицы персон

 CREATE TABLE [dbo].[Person] (
    [Id]        BIGINT       IDENTITY (1, 1) NOT NULL,
    [Name]      VARCHAR (50) NOT NULL,
    [Surname]   VARCHAR (50) NOT NULL,
    [BirthDate] DATE         DEFAULT (getdate()) NOT NULL
);
  

Консольное приложение .NET Core

 public class Person
{
    public uint Id { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public DateTime BirthDate { get; set; }
}
public class PeopleDataConnection : DataConnection
{
    public PeopleDataConnection(string connectionString)
        : base(ProviderName.SqlServer, connectionString)
    { }

    public ITable<Person> People => this.GetTable<Person>();
}
class Program
{
    static async Task Main(string[] args)
    {
        await using (var db = new PeopleDataConnection("Server=DESKTOP-I41VSUN;integrated Security=SSPI;Database=MotoGPRiders;"))
        {
            var dynamicPerson = new { Name = "Cal", Surname = "Crutchlow", BirthDate = new DateTime(1985, 10, 29) };
            var person_without_id = new Person { Name = "Valentino", Surname = "Rossi", BirthDate = new DateTime(1979, 2, 16) };

            var affectedRowsCount = await db.InsertAsync(dynamicPerson, nameof(Person));
            affectedRowsCount = await db.People
                .Value(x => x.Name, person_without_id.Name)
                .Value(x => x.Surname, person_without_id.Surname)
                .Value(x => x.BirthDate, person_without_id.BirthDate)
                .InsertAsync();
        }
        Console.ReadLine();
    }
}
  

Первая вставка выполняет задание, но не может вернуть Id , вторая позволяет мне выбирать столбцы, но не динамически.

Есть ли способ добиться этого? Заранее спасибо!

Ответ №1:

Вы можете получить значение идентификатора с помощью InsertWithInt64IdentityAsync функции. Для такой вставки много перегрузок:

 var insertedId = await db.People
            .Value(x => x.Name, person_without_id.Name)
            .Value(x => x.Surname, person_without_id.Surname)
            .Value(x => x.BirthDate, person_without_id.BirthDate)
            .InsertWithInt64IdentityAsync();
  

Также вы можете отфильтровать свойства, которые должны быть вставлены через перегрузку, которая имеет columnFilter параметр:

 var insertedId = await db.InsertAsync(person, 
    e => e.MemberName.In("Name", "Surname", "BirthDate"));
  

Для варианта с анонимным классом вам необходимо создать свою собственную функцию, которая генерирует вызовы посредством отражения с использованием Value.Value.Value InsertWithInt64IdentityAsync вызовов:

 public static class DynamicExtensions
{
    public static IValueInsertable<T> GenerateInsertable<T>(this ITable<T> table, object obj)
    {
        var dynamicAccessor = TypeAccessor.GetAccessor(obj.GetType());
        var accessor = TypeAccessor.GetAccessor<T>();
        var objValue = Expression.Constant(obj);
        IValueInsertable<T> insertable = null;
        foreach (var dynamicMember in dynamicAccessor.Members)
        {
            var found = accessor.Members.Find(m => m.Name == dynamicMember.Name);
            if (found == null)
                continue;
            var sourceParam = Expression.Parameter(typeof(T), "e");
            var property = Expression.Lambda(Expression.MakeMemberAccess(sourceParam, found.MemberInfo),
                sourceParam);
            Expression value = Expression.MakeMemberAccess(objValue, dynamicMember.MemberInfo);
            if (value.Type != found.Type)
                value = Expression.Convert(value, found.Type);
            value = Expression.Lambda(value);

            if (insertable == null)
            {
                var method = Methods.LinqToDB.Insert.T.ValueExpression.MakeGenericMethod(typeof(T), found.Type);
                insertable = (IValueInsertable<T>)method.Invoke(null, new object[] { table, property, value });
            }
            else
            {
                var method = Methods.LinqToDB.Insert.VI.ValueExpression.MakeGenericMethod(typeof(T), found.Type);
                insertable = (IValueInsertable<T>)method.Invoke(null, new object[] { insertable, property, value });
            }
        }

        return insertable;
    }

    public static long? InsertWithIdentityDynamic<T>(this ITable<T> table, object obj)
    {
        var insertable = GenerateInsertable(table, obj);
        if (insertable == null)
            return null;
        
        return insertable.InsertWithInt64Identity();
    }

    public static async Task<long?> InsertWithIdentityDynamicAsync<T>(this ITable<T> table, object obj, CancellationToken cancellationToken = default)
    {
        var insertable = GenerateInsertable(table, obj);
        if (insertable == null)
            return null;

        return await insertable.InsertWithInt64IdentityAsync(cancellationToken);
    }
}
  

И использование:

 var insertedId = await db.GetTable<Person>()
    .InsertWithIdentityDynamicAsync(dynamicPerson);