alirezanet/Gridify

IN style operator

Closed this issue · 5 comments

Discussed in #133

We need to generate something like this:

'SELECT State FROM Tasks WHERE State IN (guid1, guid2)'

also CustomOperator for this purpose is not working:

GridifyGlobalConfiguration.CustomOperators.Register(new InOperator());
 

var repo = new List<TestTask>() 
{
  new() { State = 1 },
  new() { State = 2 },
  new() { State = 3 },
  new() { State = 4 },
}.AsQueryable();
 

var qb = new QueryBuilder<TestTask>()
	.AddMap("state", q=> q.State, value => value.Split(";").Select(int.Parse).ToList())
 	.AddCondition("state#=2;4;6")
	.Build(repo)
	.Dump();
	 

class InOperator : IGridifyOperator
{
	public string GetOperator() => "#=";
	public Expression<OperatorParameter> OperatorHandler()
	{
		return (prop, value) => ((IList)value).Contains((int)prop);
	}
}


class TestTask 
{
	public int State { get; set; }
}

Error:
ArgumentException•••
Argument types do not match

in v2.13.1, Custom operators can support type casting so now it is possible to create an In operator if needed.

e.g:

class InOperator: IGridifyOperator
{
   public string GetOperator()
   {
      return "#In";
   }

   public Expression<OperatorParameter> OperatorHandler()
   {
      return (prop, value) => value.ToString()
         .Split(";",StringSplitOptions.RemoveEmptyEntries)
         .Contains(prop.ToString());
   }
}

Or if you need to only support a specific type you can parse it using GridifyMapper's third overload, which is value converter:

e.g for int:

var fitler = "state#In2;4;6";
 
 // Here we are converting the value to a List<int> 
mapper.AddMap("state", q => q.State, value => value.Split(";").Select(int.Parse).ToList())
 
internal class IntInOperator : IGridifyOperator
{
   public string GetOperator()
   {
      return "#In";
   }

   public Expression<OperatorParameter> OperatorHandler()
   {
      return (prop, value) => ((List<int>)value).Contains((int)prop);
   }
}

Hello, Thank you for this update!

I have tried the second example using Guid's instead of integer's and I get the following error:

System.InvalidOperationException : No coercion operator is defined between types 'System.Guid' and 'System.Collections.Generic.List`1[System.Guid]'.

hi @sjblack,
Oh weird! can you share your operator?

I created a simple test project and it worked as expected, 🤔 ... maybe you forgot to update to the latest version. v2.13.1?

GridifyGlobalConfiguration.CustomOperators.Register<GuidInOperator>();

var targetGuid = Guid.NewGuid();
var repo = new List<TestTask>()
	  {
		 new() { State = targetGuid },
		 new() { State = Guid.NewGuid() },
		 new() { State = Guid.NewGuid() },
		 new() { State = Guid.NewGuid() },
	  }.AsQueryable();

var ids = new List<Guid> { Guid.NewGuid(), targetGuid, Guid.NewGuid() };
var expected = repo.Where(x => ids.Contains(x.State)).ToList();

// act
var actual = new QueryBuilder<TestTask>()
   .AddMap("state", q => q.State,
	  value => value.Split(";", StringSplitOptions.RemoveEmptyEntries).Select(Guid.Parse).ToList())
   .AddCondition($"state #In {Guid.NewGuid()};{Guid.NewGuid()};{targetGuid}")
   .Build(repo)
   .ToList();
   
Debug.Assert(actual.Count == 1);
actual.Dump();

class GuidInOperator : IGridifyOperator
{
	public string GetOperator()
	{
		return "#In";
	}
	public Expression<OperatorParameter> OperatorHandler()
	{
		return (prop, value) => ((List<Guid>)value).Contains((Guid)prop);
	}
}

internal class TestTask
{
	public Guid State { get; set; }
}

Apologises I made a very silly mistake, couldn't see the wood for the tree. Works a treat, thank you very much!