I am using c# 7.3 with new feature to create a generic method where the type should be an enum.
I have a method like this:
public static bool TryConvertToEnum<T>(this int value, out T returnedValue)
where T : struct, Enum
{
if (Enum.IsDefined(typeof(T), value))
{
returnedValue = (T)Enum.ToObject(typeof(T), value);
return true;
}
returnedValue = default;
return false;
}
It will try to convert an int to a specific enum. I am trying to use this method in two cases. One does work while the other case doesn't.
Here is the working example:
if (documentTypeId.TryConvertToEnum(out DocumentType returnedValue)
&& returnedValue == DocumentType.Folder)
{
//In this case it works fine
}
If I try to use this in a select method it does not work:
var comments = await DatabaseService.GetAll(filter)
.OrderByDescending(x => x.Id)
.ToPaginated(page)
.Select(x => new PostCommentViewModel
{
Id = x.Id,
Status = x.Status.TryConvertToEnum(out PostCommentStatusType returnedValue) ?
returnedValue : PostCommentStatusType.None //Here it does not work
}).ToListAsync();
In the second case it does not allow the project to be build. It gives the error:
An expression tree may not contain an out argument
variable declaration
When I hover, RSharper does show a popup stating: Expression tree may not contain an out argument variable declaration
I am little confused to the part may, not sure if expression tree can have or not out params...
Does anybody have any idea why this happens ?
It seemed to be an easy fix actually. I only had to materialize data before applying select function :(palmhand).
var comments = DatabaseService.GetAll(filter)
.OrderByDescending(x => x.Id)
.ToPaginated(page)
.ToList()//Applied ToList here
.Select(x => new PostCommentViewModel
{
Id = x.Id,
Comment = x.Comment,
Created = x.Created,
Name = x.Name,
ParentId = x.ParentId,
PostId = x.PostId,
Status = x.Status.TryConvertToEnum(out PostCommentStatusType returnedValue) ?
returnedValue : PostCommentStatusType.None
}).ToList();
Related
It's possible to declare a lambda function and immediately call it:
Func<int, int> lambda = (input) => { return 1; };
int output = lambda(0);
I'm wondering if it's possible to do so in one line, e.g. something like
int output = (input) => { return 1; }(0);
which gives a compiler error "Method name expected". Casting to Func<int, int> doesn't work either:
int output = (Func<int, int>)((input) => { return 1; })(0);
gives the same error, and for reasons mentioned below I'd like to avoid having to explicitly specify the input argument type (the first int).
You're probably wondering why I want to do this, instead of just embedding the code directly, e.g. int output = 1;. The reason is as follows: I've generated a reference for a SOAP webservice with svcutil, which because of the nested elements generates extremely long class names, which I'd like to avoid having to type out. So instead of
var o = await client.GetOrderAsync(request);
return new Order {
OrderDate = o.OrderDate,
...
Shipments = o.Shipment_Order == null ? new Shipment[0]
o.Shipment_Order.Select(sh => new Shipment {
ShipmentID = sh.ShipmentID,
...
Address = CreateAddress(sh.ReceiverAddress_Shipment);
}).ToArray()
};
and a separate CreateAddress(GetOrderResultOrderShipment_OrderShipmentShipment_Address address) method (real names are even longer, and I have very limited control about the form), I'd like to write
var o = await client.GetOrderAsync(request);
return new Order {
OrderDate = o.OrderDate,
...
Shipments = o.Shipment_Order == null ? new Shipment[0]
o.Shipment_Order.Select(sh => new Shipment {
ShipmentID = sh.ShipmentID,
...
Address = sh.ReceiverAddress_Shipment == null ? null : () => {
var a = sh.ReceiverAddress_Shipment.Address;
return new Address {
Street = a.Street
...
};
}()
}).ToArray()
};
I know I could write
Address = sh.ReceiverAddress_Shipment == null ? null : new Address {
Street = sh.ReceiverAddress_Shipment.Address.Street,
...
}
but even that (the sh.ReceiverAddress_Shipment.Address part) becomes very repetitive if there are many fields. Declaring a lambda and immediately calling it would be more elegant less characters to write.
Instead of trying to cast the lambda, I propose you use a small helper function:
public static TOut Exec<TIn, TOut>(Func<TIn, TOut> func, TIn input) => func(input);
which you could then use like this: int x = Exec(myVar => myVar + 2, 0);. This reads a lot nicer to me than the alternatives suggested here.
It's ugly, but it's possible:
int output = ((Func<int, int>)(input => { return 1; }))(0);
Anonymous functions, including lambda expressions, are implicitly convertible to a delegate that matches their signature, but this syntax requires the lambda to be enclosed in parentheses.
The above can be simplified as well:
int output = ((Func<int, int>)(input => 1))(0);
Lambda literals in C# have a curious distinction in that their meaning is dependent on their type. They are essentially overloaded on their return type which is something does not exist anywhere else in C#. (Numeric literals are somewhat similar.)
The exact same lambda literal can either evaluate to an anonymous function that you can execute (i.e. a Func/Action) or an abstract representation of the operations inside of the Body, kind of like an Abstract Syntax Tree (i.e. a LINQ Expression Tree).
The latter is, for example, how LINQ to SQL, LINQ to XML, etc. work: the lambdas do not evaluate to executable code, they evaluate to LINQ Expression Trees, and the LINQ provider can then use those Expression Trees to understand what the body of the lambda is doing and generate e.g. a SQL Query from that.
In your case, there is no way for the compiler to know wheter the lambda literal is supposed to be evaluated to a Func or a LINQ Expression. That is why Johnathan Barclay's answer works: it gives a type to the lambda expression and therefore, the compiler knows that you want a Func with compiled code that executes the body of your lambda instead of an un-evaluated LINQ Expression Tree that represents the code inside the body of the lambda.
You could inline the declaration of the Func by doing
int output = (new Func<int, int>(() => { return 1; }))(0);
and immediately invoking it.
You can also create the alias in the Select method
var o = await client.GetOrderAsync(request);
return new Order {
OrderDate = o.OrderDate,
...
Shipments = o.Shipment_Order == null ? new Shipment[0]
o.Shipment_Order.Select(sh => {
var s = sh.ReceiverAddress_Shipment;
var a = s.Address;
return new Shipment {
ShipmentID = sh.ShipmentID,
...
Address = s == null ?
null :
new Address {
Street = a.Street
...
}
};
}).ToArray()
};
or with the ?? operator
var o = await client.GetOrderAsync(request);
return new Order {
OrderDate = o.OrderDate,
...
Shipments = o.Shipment_Order?.Select(sh => {
var s = sh.ReceiverAddress_Shipment;
var a = s.Address;
return new Shipment {
ShipmentID = sh.ShipmentID,
...
Address = s == null ?
null :
new Address {
Street = a.Street
...
}
};
}).ToArray() ?? new Shipment[0]
};
If you don't mind violating a few of the extension methods design guidelines, extension methods combined with null-conditional operator ?. can take you reasonably far:
public static class Extensions
{
public static TOut Map<TIn, TOut>(this TIn value, Func<TIn, TOut> map)
where TIn : class
=> value == null ? default(TOut) : map(value);
public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T> items)
=> items ?? Enumerable.Empty<T>();
}
will give you this:
return new Order
{
OrderDate = o.OrderDate,
Shipments = o.Shipment_Order.OrEmpty().Select(sh => new Shipment
{
ShipmentID = sh.ShipmentID,
Address = sh.ReceiverAddress_Shipment?.Address.Map(a => new Address
{
Street = a.Street
})
}).ToArray()
};
and if you mostly need arrays, then override ToArray extension method to encapsulate a few more method calls:
public static TOut[] ToArray<TIn, TOut>(this IEnumerable<TIn> items, Func<TIn, TOut> map)
=> items == null ? new TOut[0] : items.Select(map).ToArray();
resulting in:
return new Order
{
OrderDate = o.OrderDate,
Shipments = o.Shipment_Order.ToArray(sh => new Shipment
{
ShipmentID = sh.ShipmentID,
Address = sh.ReceiverAddress_Shipment?.Address.Map(a => new Address
{
Street = a.Street
})
})
};
I have classes that drive from Base class "BaseTask"
Task1
Task2
Task3
return DbContext.Projects.Include(t => t.Tasks).Select(p => new ProjectDto
{
Id = p.Id,
Name = p.Name,
Tasks = p.Tasks.Select(t => new TaskDto()
{
Id = t.Id,
Name = t.Name,
ProjectId = t.ProjectId,
Selector = !(t is Task1) ? t.Selector : null,
Task2Property = (t is Task2) ? ((Task2)t).Task2Property : null,
SelectorPosition = (t is Task3) ? ((Task3)t).SelectorPosition : null,
KeyId = t.KeyId
}).ToList()
}
);
This part of the code returns the following error:
Unable to cast the type 'Task' to type 'Task1'. LINQ to Entities only supports casting EDM primitive or enumeration types.
how can I fix this issue?
AFAIK this is not possible with EF in that way, as EF can't translate that to SQL. But maybe a small structural change would help:
return DbContext.Projects.Include(t => t.Tasks).Select(p => new ProjectDto
{
Id = p.Id,
Name = p.Name,
Tasks = p.Tasks.Select(t => new TaskDto()
{
Id = t.Id,
Name = t.Name,
ProjectId = t.ProjectId,
Task = t,
KeyId = t.KeyId
}).ToList()
});
and in the TaskDto:
public X Selector => (Task as Task1)?.Selector;
public X Task2Property => (Task as Task2)?.Task2Property;
public X SelectorPosition => (Task as Task3)?.SelectorPosition;
Where X is the appropriate type for each property (and => the short form for { get { return x; } } if you are still using an older version of C#).
I know this is an older question, but as I was solving the same problem, I finally found out an interesting solution. Maybe it will help someone else. If you use a direct cast, it does not work, but if you use as syntax instead, EF is magically able to translate it. This should work, or at least it does in my case.
Selector = (t as Task1).Selector,
Task2Property = (t as Task2).Task2Property,
SelectorPosition = (t as Task3).SelectorPosition,
If the type is not correct, the value is null, so it has the same result you wanted to achieve.
I have the following piece of code:
var newProducts = _summaryRepository.GetFilteredSummaries(manufacturerIds, countryIds, categoryIds,
null, widgetIds, startDate, null).Where(s => s.Product.ProductCountries.FirstOrDefault(pc => pc.CountryId == s.CountryId).CreatedAt >= startDate.Value)
.GroupBy(s => new { widgetId = s.widgetId, ProductId = s.ProductId });
If I have the condition Show all I want to take the following out of the GroupBY:
WidgetId = s.WidgetId
So the it would now be:
var newProducts = _summaryRepository.GetFilteredSummaries(manufacturerIds, countryIds, categoryIds,
null, widgetIds, startDate, null).Where(s => s.Product.ProductCountries.FirstOrDefault(pc => pc.CountryId == s.CountryId).CreatedAt >= startDate.Value)
.GroupBy(s => new {ProductId = s.ProductId });
There is a lot of code which is reliant on newProducts and when I have created an if statement and put the var newProducts in the outerscrope it stops everything working.
I know this is probably a silly question but how can I do this with the minimal repetitive code?
Is it that I'm declaring the variable wrong when I do this:
var newProducts;
if(model.allWidgets)
{newProducts = _summaryRepository.GetFilteredSummaries(manufacturerIds, countryIds, categoryIds,
null, WidgetIds, startDate, null).Where(s => s.Product.ProductCountries.FirstOrDefault(pc => pc.CountryId == s.CountryId).CreatedAt >= startDate.Value)
.GroupBy(s => new {ProductId = s.ProductId });}
else
{
newProducts = _summaryRepository.GetFilteredSummaries(manufacturerIds, countryIds, categoryIds,
null, WidgetIds, startDate, null).Where(s => s.Product.ProductCountries.FirstOrDefault(pc => pc.CountryId == s.CountryId).CreatedAt >= startDate.Value)
.GroupBy(s => new { WidgetId = s.WidgetId, ProductId = s.ProductId });
}
You can change your GroupBy to ignore s.WidgetId when model.allWidgets is true:
query.GroupBy(s => new { WidgetId = model.allWidgets ? 0 : s.WidgetId, ProductId = s.ProductId });
From LINQ Conditional Group, you can add a "null" to the grouping (WidgetId = 0), causing the GroupBy to return the same anonymous grouping type in both cases:
var newProducts = _summaryRepository.GetFilteredSummaries(...)
.Where(s => ...)
var groupedProducts = newProducts.GroupBy(s =>
{
if(model.allWidgets)
{
return new
{
ProductId = s.ProductId,
WidgetId = 0,
};
}
else
{
return new
{
ProductId = s.ProductId,
WidgetId = s.WidgetId,
};
}
});
And of course, as the just-added answer indicates, this can be greatly reduced using the conditional operator:
var groupedProducts = newProducts.GroupBy(s =>
new
{
ProductId = s.ProductId,
WidgetId = model.allWidgets ? 0 : s.WidgetId,
});
This type is still anonymous and thus not directly accessible from your code, so in order to return it from a method, introduce a class to hold the group:
public class ProductGrouping
{
public int ProductId { get; set; }
public int? WidgetId { get; set; }
}
public IGrouping<ProductGrouping, Summary> GetGroupedSummaries()
{
return _summaryRepository.GetFilteredSummaries(...)
.Where(s => ...)
.GroupBy(s => new ProductGrouping
{
ProductId = s.ProductId,
WidgetId = model.allWidgets ? (int?)null : s.WidgetId,
});
}
What you are trying to do is not really possible because your 2 queries return different anonymous types, unless you are willing to assign the results back into a variable of type object, which doesn't seem very useful (or dynamic, but then you lose the compile time benefits of using LINQ).
To be able to use the var keyword, the compiler must be able to determine the type on declaration, which means you have to provide an assignment right where you declare your variable.
As stated in the documentation for Implicitly Typed Local Variables:
The var keyword instructs the compiler to infer the type of the variable from the expression on the right side of the initialization statement.
And also...
It is important to understand that the var keyword does not mean "variant" and does not indicate that the variable is loosely typed, or late-bound. It just means that the compiler determines and assigns the most appropriate type.
I understand that LINQ can't use properties that are not mapped to a database column, though I don't understand why one LINQ statement works inside a non static method but I get this error when attempting within one.
Here's my working method:
private TemplatesAPIContext db = new TemplatesAPIContext();
// GET api/Template
public IQueryable<TemplateDto> GetTemplates()
{
return db.TemplateModels.Include(t => t.Categories).Select(
x => new TemplateDto
{
TemplateID = x.TemplateID,
Name = x.Name,
HTMLShowcase = x.HTMLShowcase,
ShortDescription = x.ShortDescription,
CreationDate = x.CreationDate,
Downloads = x.Downloads,
Tags = x.Tags,
Categories = db.CategoryModels
.Where(c => x.Categories.Where(a => a.TemplateID == x.TemplateID)
.Select(a => a.CategoryID).Contains(c.CategoryID))
}
);
}
I don't want to repeat myself with this complex building of a DTO (I actually still need to add some other relationships still to it and it will get much more complex) and type this out on every method in the controller so I wanted to make a lambda expression and pass it to the methods.
So I did this:
private static readonly Expression<Func<TemplateModel, TemplateDto>> AsTemplateDto =
x => new TemplateDto
{
TemplateID = x.TemplateID,
Name = x.Name,
HTMLShowcase = x.HTMLShowcase,
ShortDescription = x.ShortDescription,
CreationDate = x.CreationDate,
Downloads = x.Downloads,
Tags = x.Tags,
Categories = new TemplatesAPIContext().CategoryModels
.Where(c => x.Categories.Where(a => a.TemplateID == x.TemplateID)
.Select(a => a.CategoryID).Contains(c.CategoryID))
};
In the hopes of calling:
// GET api/Template
public IQueryable<TemplateDto> GetTemplates()
{
return db.TemplateModels.Include(t => t.Categories).Select(AsTemplateDto);
}
But this returns this error, which doesn't make sense to me since its the exact same query, only difference being that I need to instantiate the dbContext in the lambda since I can't use the one instantiated in the controller as the lambda expression is static.
Error
The specified type member 'CategoryModels' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
It's important that the same context be used within the query as the one that's making the query, for the query provider to understand what you're trying to do. So all you need is a way of making a copy of that expression that's specific to a given context, which isn't that hard, you've done almost all of the work.
//TODO rename method as appropriate
private static Expression<Func<TemplateModel, TemplateDto>>
CreateTemplateDTO(TemplatesAPIContext context)
{
return x => new TemplateDto
{
TemplateID = x.TemplateID,
Name = x.Name,
HTMLShowcase = x.HTMLShowcase,
ShortDescription = x.ShortDescription,
CreationDate = x.CreationDate,
Downloads = x.Downloads,
Tags = x.Tags,
Categories = context.CategoryModels
.Where(c => x.Categories.Where(a => a.TemplateID == x.TemplateID)
.Select(a => a.CategoryID).Contains(c.CategoryID))
};
}
Now you can write:
public IQueryable<TemplateDto> GetTemplates()
{
return db.TemplateModels.Include(t => t.Categories)
.Select(CreateTemplateDTO(db));
}
Your first method is a simple expression tree that contains only simple operations in tree nodes (like assign A to B) thus it can easily be compiled into SQL query.
The other method contains instantiation of TemplatesAPIContext. It's not possible for database query.
I have the following LINQ method that works as expected except if there are No Rows Found then I get a Null Exception. I am struggling on how I modify this to return 0 if that occurs.
public static int GetLastInvoiceNumber(int empNumber)
{
using (var context = new CmoDataContext(Settings.Default.LaCrosse_CMOConnectionString))
{
context.Log = Console.Out;
IQueryable<tblGreenSheet> tGreenSheet = context.GetTable<tblGreenSheet>();
return (tGreenSheet
.Where(gs => gs.InvoiceNumber.Substring(2, 4) == empNumber.ToString())
.DefaultIfEmpty()
.Max(gs => Convert.ToInt32(gs.InvoiceNumber.Substring(6, gs.InvoiceNumber.Length)))
);
}
}
Thanks
I tried one of Jon Skeet's suggestions, below, and now I get Unsupported overload used for query operator 'DefaultIfEmpty'
public static int GetLastInvoiceNumber(int empNumber)
{
using (var context = new CmoDataContext(Settings.Default.LaCrosse_CMOConnectionString))
{
context.Log = Console.Out;
IQueryable<tblGreenSheet> tGreenSheet = context.GetTable<tblGreenSheet>();
return tGreenSheet
.Where(gs => gs.InvoiceNumber.Substring(2, 4) == empNumber.ToString())
.Select(gs => Convert.ToInt32(gs.InvoiceNumber.Substring(6, gs.InvoiceNumber.Length)))
.DefaultIfEmpty(0)
.Max();
}
}
You're using
.Where(...)
.DefaultIfEmpty()
which means if there are no results, pretend it's a sequence with a single null result. You're then trying to use that null result in the Max call...
You can probably change it to:
return tGreenSheet.Where(gs => ...)
.Max(gs => (int?) Convert.ToInt32(...)) ?? 0;
This uses the overload finding the maximum of int? values - and it returns an int? null if there were no values. The ?? 0 then converts that null value to 0. At least, that's the LINQ to Objects behaviour... you'll have to check whether it gives the same result for you.
Of course, you don't need to use the ?? 0 if you're happy to change the method signature to return int? instead. That would give extra information, in that the caller could then tell the difference between "no data" and "some data with a maximum value of 0":
return tGreenSheet.Where(gs => ...)
.Max(gs => (int?) Convert.ToInt32(...));
Another option is to use the overload of DefaultIfEmpty() which takes a value - like this:
return tGreenSheet.Where(gs => ...)
.Select(gs => Convert.ToInt32(...))
.DefaultIfEmpty(0)
.Max();
In situations like this when there may or may not be a matching item, I prefer to return an object rather than a value type. If you return a value type, you have to have some semantics about what value means "there is nothing here." I would change it to return the last invoice, then (when it is non-null) get the invoice number from the invoice. Add a method to the class to return the numeric invoice number from the string.
public static tbleGreenSheet GetLastInvoice(int empNumber)
{
using (var context = new CmoDataContext(Settings.Default.LaCrosse_CMOConnectionString))
{
context.Log = Console.Out;
return context.GetTable<tblGreenSheet>()
.Where(gs => gs.InvoiceNumber.Substring(2, 4) == empNumber.ToString())
.OrderByDescending(gs => Convert.ToInt32(gs.InvoiceNumber.Substring(6, gs.InvoiceNumber.Length)))
.FirstOrDefault();
}
}
public class tbleGreenSheet
{
....
public int NumericInvoice
{
get { return Convert.ToInt32(InvoiceNumber.Substring(6, InvoiceNumber.Length)); }
}
...
}
Used as
var invoice = Foo.GetLastInvoice( 32 );
if (invoice != null)
{
var invoiceNumber = invoice.NumericInvoice;
...do something...
}
else
{
...do something else...
}
I had a remarkably similar experience with IQueryable<T> and NHibernate. My solution:
public static TExpr MaxOrDefault<TItem, TExpr>(this IQueryable<TItem> query,
Expression<Func<TItem, TExpr>> expression) {
return query.OrderByDescending(expression).Select(expression).FirstOrDefault();
}
The only drawback is that you are stuck with the standard default value, instead of getting to specify one.