Get Right value from LanguageExt c# - c#

I have method that returns:
Either<Error, (int seconds, int count)> result = GetResult();
I need to return right value: (int NextDelayMs, int NextRetryCount)
But I can't figure out how to do it.
I tried:
var toReturn = result.Map(x => x.Right);
but I get IEnumerable this way. What am I doing wrong?

You have to handle the left case as an Either value can be left type L or right type R.
If you want to throw an exception when result is an Error you can use this:
var toReturn = result.IfLeft(err => throw err.ToException());
But you should avoid this. It's better to stay within Either using Map/Select and pass any errors to the calling method:
var errorOrFunctionResult = result.Map(x => someFunction(x));
If you want to supply a fallback value in case of an error:
var toReturn = result.IfLeft((100, 0))
Basically Either is similar to Option regarding usage and you can find a good introduction here: https://github.com/louthy/language-ext/#option

Related

In C#, how can I create a value type variable at runtime?

I am attempting to implement a method like:
(Func<T> getFn, Action<T> setFn) MakePair<T>(T initialVal) {
}
It will return two runtime generated lambdas that get and set a dynamically created variable using Expression trees to create the code.
My current solution is to dynamically create an array of the type with one element, and reference that:
(Func<T> getFn, Action<T> setFn) MakePair<T>(T initialVal) {
var dynvar = Array.CreateInstance(typeof(T), 1);
Expression<Func<Array>> f = () => dynvar;
var dynref = Expression.Convert(f.Body, typeof(T).MakeArrayType());
var e0 = Expression.Constant(0);
var getBody = Expression.ArrayIndex(dynref, e0);
var setParam = Expression.Parameter(typeof(T));
var setBody = Expression.Assign(Expression.ArrayAccess(dynref, e0), setParam);
var getFn = Expression.Lambda<Func<T>>(getBody).Compile();
var setFn = Expression.Lambda<Action<T>>(setBody, setParam).Compile();
return (getFn, setFn);
}
Is there a better way to create what may be a value type variable at runtime that can be read/written to than using an array?
Is there a better way to reference the runtime created array other than using a lambda to create the (field?) reference for use in the ArrayIndex/ArrayAccess method calls?
Excessive Background Info
For those that wonder, ultimately this came up in an attempt to create something like Perl auto-virification of lvalues for Perl hashes.
Imagine you have a List<T> with duplicate elements and want to create a Dictionary<T,int> that allows you to look up the count for each unique T in the list. You can use a few lines of code to count (in this case T is int):
var countDict = new Dictionary<int, int>();
foreach (var n in testarray) {
countDict.TryGetValue(n, out int c);
countDict[n] = c + 1;
}
But I want to do this with LINQ, and I want to avoid double-indexing countDict (interestingly, ConcurrentDictionary has AddOrUpdate for this purpose) so I use Aggregate:
var countDict = testarray.Aggregate(new Dictionary<int,int>(), (d, n) => { ++d[n]; return d; });
But this has a couple of issues. First, Dictionary won't create a value for a missing value, so you need a new type of Dictionary that auto-creates missing values using e.g. a seed lambda:
var countDict = testarray.Aggregate(new SeedDictionary<int, Ref<int>>(() => Ref.Of(() => 0)), (d, n) => { var r = d[n]; ++r.Value; return d; });
But you still have the lvalue problem, so you replace the plain int counter with a Ref class. Unfortunately, C# can't create a C++ first class Ref class, but using one based around auto-creating a setter lambda from a getter lambda (using expression trees) is close enough. (Unfortunately C# still won't accept ++d[n].Value; even though it should be valid, so you have to create a temporary.)
But now you have the problem of creating multiple runtime integer variables to hold the counts. I extended the Ref<> class to take a lambda that returns a constant (ConstantExpression) and create a runtime variable and build a getter and setter with the constant being the initial value.
I agree with some of the question commenters that expression trees seem unnecessary, so here is a simple implementation of the shown API without them:
struct Box<T> {
public T Value;
}
(Func<T> getFn, Action<T> setFn) MakePair<T>(T initialVal) {
var box = new Box<T> { Value = initialVal };
return (() => box.Value, v => box.Value = v);
}
As an answer to the stated question (how to define dynref without a lambda), then, is there something wrong with the following modifications to dynvar and dynref?
var dynvar = new T[] { initialVal };
var dynref = Expression.Constant(dynvar);

c# Linq expression builder Expression.Equal showing parms around the wrong way

I am trying to build some dynamic Linq using expressions for an advance search facility.
I call my define expression builder like so:
IQueryable<DocPod> retval = Enumerable.Empty<Doc>().AsQueryable();
retval = DefineSearchExpression(x => x.Id,
searchCriteria.searchData.Comparison,
searchCriteria.searchData.NumberFrom,
searchCriteria.searchData.NumberTo);
To explain the above, I have multiple properties on a page where each property has a drop down providing various search types, so in the case above I have an int called Id, then a comparison which can be things like equals, not equals, greater than, etc... Then depending on the selected comparison, NumberFrom is the main search parameter and should always be populated, but in the case of between comparison, the NumberTo parameter is also populated.
The function DefineSearchExpression, is shown below:
private IQueryable<Docs> DefineSearchExpression(Expression<Func<Docs, object>> searchColumnName, int compare, object minValue, object maxValue)
{
IQueryable<Docs> retval = Entities.Docs.AsQueryable();
// We must have a minValue to process
if (minValue == null)
{
return retval;
}
// LINQ Expression that represents the column passed in searchColumn
var columnExpression = GetMemberExpression(searchColumnName);
// LINQ Expression to represent the parameter of the lambda you pass in
ParameterExpression parameterExpression = (ParameterExpression)columnExpression.Expression;
// Expressions to represent min and max values
Expression minValueExpression = null;
Expression maxValueExpression = null;
Expression minComparisonExpression = null;
Expression maxComparisonExpression = null;
// Represents the completed filter
Expression<Func<Docs, bool>> filterLambdaExpression = null;
// setup vars
if (minValue.IsNumeric())
{
var intfromValue = int.Parse(minValue.ToString());
minValueExpression = Expression.Constant(intfromValue);
if (maxValue != null)
{
var inttoValue = int.Parse(maxValue.ToString());
maxValueExpression = Expression.Constant(inttoValue);
}
}
else if (minValue.IsDate())
{
var dtfromValue = DateTime.Parse(minValue.ToString());
minValueExpression = Expression.Constant(dtfromValue);
if (maxValue != null)
{
var dttoValue = DateTime.Parse(maxValue.ToString());
maxValueExpression = Expression.Constant(dttoValue);
}
}
// Expression represented by selection in dropdown List
switch (compare)
{
//
// THIS IS WHERE THE FIRST ISSUES IS LOCATED
//
//
case 0: // This should be equivalent to: field = value
minComparisonExpression = Expression.Equal(minValueExpression, columnExpression);
filterLambdaExpression = Expression.Lambda<Func<Docs, bool>>(minComparisonExpression, parameterExpression);
break;
}
retval = retval.AsQueryable().Where(filterLambdaExpression);
return retval.AsQueryable();
}
As you can see, I am trying to take into account numeric, datetime and finally string data for searching.
When the above code is executed, when the code within the switch Case 0 provides the following anomaly.
minComparisonExpression has the debug value of 1977836648 == $x.Id
I have tried to swap these values around, but with little success, if anyone knows how to fix this, I would be very grateful.
Secondly, when DefineSearchExpression returns, the new expression is lost, this is most probably me, but I just cannot see it.
Oops. Thank you for spotting the error, but that does not help as the offending code is in the Switch Case 0 statements, the date code is not run at all at the moment.
Code updated!

linq to entity query getting error

I am trying to amend a c# project. I am a vb.vet programmer so having a few issues as I am new to linq. I am trying to run a Linq to Entity query. I want to select the MapEast where town = town. I keep get an error The specified cast from a materialized System.Decimal' type to the 'System.Int32' type is not valid.. I would like to put a max(1) in here too so it returns only the highest number.
var topEast = 0;
try
{
topEast = this._uow.Addresses
.Where(a =>
a.Town.Trim().ToUpper() == town.Trim().ToUpper())
.Select(m => m.MapEast).FirstOrDefault ();
return -1;
}
catch
{
return -1;
}
Thanks
var is used for implicitly typed local variable. When you defined var topEast = 0;, topEast was implicitly assigned type int, and not decimal as per your query. You can fix it by explicitly defining topEast as decimal.
decimal topEast = 0;
I would like to put a max(1) in here too so it returns only the
highest number.
Not really sure what you are trying to return, because you are returning -1 from try as well as catch block. If you are trying to return the Max value of MapEast field then you will need Enumerable.Max, otherwise FirstOrDefault would return the first item or null based on criteria.

How to expand calling expressions other than predicates?

I just discovered LINQKit and am quite happy that it seems to provide a solution for the common problem of wanting to factor out parts of complex linq queries.
All examples, however, show how to factor out predicates for where-clauses.
Although that's a very typical use-case, I also want to factor out other kinds of expressions, typically for selects. Let's say I have the following sub-expression:
Expression<Func<SomeFunkyEntity, String>> GetStatusFromSomeFunkyEntity()
{
return i =>
i.IsExercise ? "Excercise" :
i.IsExpired ? "Expired" :
(i.IsLocked ) ? "Locked" :
i.IsAdmitted ? "Admissible" : "Unusable";
}
Does LINQKit provide a way to expand a call to this? I tried:
var query =
from i in this.DbContext.MyFunkyEntities.AsExpandable()
select new SomeFunkyEntityWithStatus()
{
FunkyEntity = i,
Status = GetStatusFromSomeFunkyEntity().Invoke(i)
};
This complies, but fails at runtime in LINQKit's expander:
Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.LambdaExpression'.
The stack trace starts with:
at LinqKit.ExpressionExpander.VisitMethodCall(MethodCallExpression m)
at LinqKit.ExpressionVisitor.Visit(Expression exp)
at LinqKit.ExpressionVisitor.VisitMemberAssignment(MemberAssignment assignment)
...
Is this not supported, or do I do something wrong?
Have you tried to assign the expression to a local variable before calling invoke?
var statusFromSomeFunkyEntity = GetStatusFromSomeFunkyEntity();
var query =
from i in this.DbContext.MyFunkyEntities.AsExpandable()
select new SomeFunkyEntityWithStatus()
{
FunkyEntity = i,
Status = statusFromSomeFunkyEntity.Invoke(i)
};
Check out this answer.

How to Assign a Query Result to an Integer Array

public List<Workflow> GetMyWorkflows(int[] MyRoles)
{
int[] myWorkflowIDs = new int[] { };
RapidWorkflowDataContext context = new RapidWorkflowDataContext();
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select w.WorkflowID;
var distinctWorkflows = query.Distinct();
myWorkflowIDs = distinctWorkflows.toArray();
return myWorkflowIDs;
}
In this method I want to retrieve an array of workflows that a user can
access.
I get the following error : Cannot implicitly convert type 'int?[]' to 'int[]'
I want to retrieve an array of workflows
But your method must return a List<Workflow> or a List<int>.
So you should skip the array idea. The other issue is between int and int?. You can solve that in the select clause with select w.WorkflowID.Value or select w.WorkflowID ?? 0. Or simply select w for a List<Workflow>.
Also it is a good idea to dispose a context when it becomes unreachable.
public List<int> GetMyWorkflows(int[] MyRoles)
{
using (RapidWorkflowDataContext context = new RapidWorkflowDataContext())
{
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select w.WorkflowID ?? 0;
// select w; to return a List<WorkFlow>
var distinctWorkflows = query.Distinct();
return distinctWorkflows.ToList(); // ToList because we are closing the Context
}
}
I'm going to guess that WorkflowID is of type int?. If you are certain that it cannot be null, change your central query to:
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select w.WorkflowID.Value;
This will ensure that query is now of type IEnumerable<int> instead of IEnumerable<int?>, with the int following on throuhh the Distinct() and ToArray() functions.
This seems like a pretty good error to me
Cannot convert type 'int?[]' to 'int[]'
You must have an array of type int? and be trying to implicitly convert it to int.
Therefore you have two options - stop trying to implicitly convert, and allow the result to be int?[], like this:
int?[] myWorkflowIDs = new int?[] { };
or force the convert to take place, like this:
RapidWorkflowDataContext context = new RapidWorkflowDataContext();
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select (int)w.WorkflowID;
// or w.WorkflowID ?? 0; as necessary
So int? can also be written Nullable<int> which is basically an int that can take null values. For example:
int? nullableNumber = 5; // Set to a value
nullableNumber = null? // Set to null (which is possible because int? is nullable).
As you can imagine, Nullable<int> is useful for databases because sometimes you might have a column that has null values, and so this type gives a useful means of mapping to this sort of value. The problem, though is that in your code you have to deal with two different types, int vs. int?. You can cast between the two values by using:
// If the nullable-integer is not-null then use it's value, else default to `0`.
int nonNullable = nullableNumber ?? 0;
which will replace nulls with 0 if the value is null. Or you can just store your myWorkflowIDs in a nullable value (Nullable<int>[] or int?[]), which semantically better reflects what the column value in the database actually is.

Categories