Open YeskaNova opened 3 years ago
Hello @YeskaNova,
This should work when just selecting:
List<Customer> list = new List<Customer>();
var d = new Dictionary<string, string>();
d.Add("a", "aa");
list.Add(new Customer
{
Array = new int[] { 1 },
Data = d
});
var x = list.AsQueryable().Select("new (Array[0] as a, Data[\"a\"] as d)").AsEnumerable();
foreach (var val in x)
{
Console.WriteLine(val);
}
https://dotnetfiddle.net/pHw3BN
Does this help you?
@StefH What I need is to assign data to the dictionary because I want to use it with an .UpdateFromQuery()
And will this help you?
var expr1 = DynamicExpressionParser.ParseLambda(typeof(Customer), typeof(Customer), "new ( @0 as Data )", new Dictionary<string, string> { { "a", "b" } });
var com1 = expr1.Compile();
var c1 = com1.DynamicInvoke(new Customer());
I have this exception when I try to run it:
System.ArgumentNullException
HResult=0x80004003
Message=Value cannot be null. (Parameter 'expression')
Source=System.Linq.Expressions
StackTrace:
at System.Dynamic.Utils.ContractUtils.RequiresNotNull(Object value, String paramName, Int32 index)
at System.Dynamic.Utils.ExpressionUtils.RequiresCanRead(Expression expression, String paramName, Int32 idx)
at System.Linq.Expressions.Expression.Bind(MemberInfo member, Expression expression)
at System.Linq.Dynamic.Core.Parser.ExpressionParser.CreateNewExpression(List`1 properties, List`1 expressions, Type newType)
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseNew()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseIdentifier()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParsePrimaryStart()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParsePrimary()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseUnary()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseMultiplicative()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAdditive()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseShiftOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseComparisonOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseLogicalAndOrOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseIn()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAndOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseOrOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseLambdaOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseNullCoalescingOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseConditionalOperator()
at System.Linq.Dynamic.Core.Parser.ExpressionParser.Parse(Type resultType, Boolean createParameterCtor)
at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Type delegateType, ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values)
at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values)
at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values)
at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Boolean createParameterCtor, Type itType, Type resultType, String expression, Object[] values)
at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Type itType, Type resultType, String expression, Object[] values)
Code:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
public class Customer
{
public int Id { get; set; }
public Dictionary<string, object> Data { get; set; }
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var etb = modelBuilder.Entity<Customer>();
etb.OwnsOne(typeof(Dictionary<string, object>).Name, x => x.Data, x =>
{
x.IndexerProperty(typeof(string), "a").IsRequired(false);
x.IndexerProperty(typeof(string), "b").IsRequired(false);
x.Property<int>("CustomerId");
});
}
}
class Program
{
static void Main(string[] args)
{
var sc = new ServiceCollection();
var guid = Guid.NewGuid();
sc.AddDbContext<AppDbContext>(op => op.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Integrated Security=true;DataBase=" + guid));
using (var provider = sc.BuildServiceProvider())
{
using (var scope = provider.CreateScope())
using (var dbcontext = scope.ServiceProvider.GetService<AppDbContext>())
{
dbcontext.Database.EnsureCreated();
}
var user = new Customer();
using (var scope = provider.CreateScope())
using (var dbcontext = scope.ServiceProvider.GetService<AppDbContext>())
{
dbcontext.Set<Customer>().Add(user);
dbcontext.SaveChanges();
}
using (var scope = provider.CreateScope())
using (var dbcontext = scope.ServiceProvider.GetService<AppDbContext>())
{
var expr = (Expression<Func<Customer, Customer>>)DynamicExpressionParser.ParseLambda(typeof(Customer), typeof(Customer), "new ( @0 as Data )", new Dictionary<string, string> { { "a", "newA" }});
dbcontext.Set<Customer>().UpdateFromQuery(expr);
dbcontext.SaveChanges();
}
}
}
}
I noticed that you use UpdateFromQuery
with the expression.
Do you also get an exception when just doing a Where clause?
I think that the exception happens when parsing the expression, it doesn't matter if it's in UpdateFromQuery because I had it also with the code you provided, Probably it's another issue.
Some observations:
I noticed that you use UpdateFromQuery
, this is from Z.EntityFramework.Extensions.EFCore
. And I'm not sure if this can work in combination with this project. @JonathanMagnan do you know this?
When using ""public Dictionary<string, object> Data { get; set; }". I get exceptions like:
When running this code:
using (var context = new MyDbContext())
{
var expr = (Expression<Func<Customer, Customer>>)DynamicExpressionParser.ParseLambda(typeof(Customer), typeof(Customer), "new ( @0 as Data )", new Dictionary<string, string> { { "a", "newA" }, { "b", "newB" }, { "TestId", "333" } });
context.Set<Customer>().UpdateFromQuery(expr);
context.SaveChanges();
}
I get this exception:
Entity Framework Core 5.0.2 initialized 'MyDbContext' using provider 'Microsoft.EntityFrameworkCore.Sqlite' with options: SensitiveDataLoggingEnabled
Unhandled exception. System.Exception: Oops! that scenario is currently unsupported for the `UpdateFromQuery` feature. You can report it here: info@zzzprojects.com
I updated my code, I have renamed the dbcontext class but this won't change the code flow.
It says that you have sqlite as the provider, could you share the dbcontext configuration ?
On my side, it still fails at the parsing:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging()
.UseSqlite("Data Source = EFCoreTour.db");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Customer>()
.OwnsOne(typeof(Dictionary<string, object>).Name, x => x.Data, x =>
{
x.IndexerProperty<string>("a").IsRequired(false);
x.IndexerProperty<string>("b").IsRequired(false);
x.Property<int?>("TestId").IsRequired(false);
});
}
@StefH , I don't think that's compatible since the anonymous type
created is not really an anonymous type.
We will check if there is something we can do about it.
Hello @StefH ,
Look like I was wrong, my developer did the test and it seems we already support anonymous type created by LINQ Dynamic.
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Text;
namespace Lab.EFCore50
{
class Request_LinqDynamic
{
public static void Execute()
{
// SEED
using (var context = new EntityContext())
{
for (int i = 0; i < 3; i++)
{
context.EntitySimples.Add(new EntitySimple { ColumnInt = i });
}
context.SaveChanges();
}
// Test
using (var context = new EntityContext())
{
var expr1 = (Expression<Func<EntitySimple, EntitySimple>>)DynamicExpressionParser.ParseLambda(typeof(EntitySimple), typeof(EntitySimple), "new ( @0 + ColumnInt.ToString() as ColumnString )", "test7");
context.EntitySimples.UpdateFromQuery(expr1);
var result = context.EntitySimples.AsNoTracking().ToList();
}
}
public class EntityContext : DbContext
{
public EntityContext()
{
}
public DbSet<EntitySimple> EntitySimples { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(new SqlConnection(My.ConnectionString));
base.OnConfiguring(optionsBuilder);
}
}
public class EntitySimple
{
public int ID { get; set; }
public int ColumnInt { get; set; }
public string ColumnString { get; set; }
}
}
}
@YeskaNova : can you confirm if the information provided in post above is sufficient for you?
@StefH The exception in the code that I have shared is raised when parsing the expression, before even running it against an IQueyrable, @JonathanMagnan confirmed that this wouldn't be a problem but we aren't there yet we need to parse it first then run it.
Hi,
In EF core, we can have Indexed properties, which are properties mapped from a dictionary to the SQL table, one column per property. I need to batch update an entity by changing one of its indexed properties but I can't make the expression-parser parse my expression. You can check it here : https://dotnetfiddle.net/9iO3hF . What I need is to have an expression that will construct a new Customer instance with a dictionary in the data property having one key-value {"lng1", "first"}. Is this currently covered by the expression parser?
Thank you.