What's wrong with this LINQ, converting one object to another? - c#

I'm trying to have a one-liner LINQ statement to project my one object to my ViewModel object - it seems it won't work with select?? The compiler says cannot resolve symbol select. This works with a collection, why doesn't it here? If not select, what keyword am I missing?
return from p in SettingRepository.Get(id)
select new EditSetting
{
};
Edit - Scratch that, doesn't work for a list. How can I accomplish this?

Although I'm not sure why you would want to use select to do this, but if you're really adamant about it:
return from p in new List<EntityObject>{SettingRepository.Get(id)}
select new EditSetting
{
};
If not, why not just use good old initializers?
var editSetting= new EditSetting { Id = setting.Id };

Typically I prefer to create methods with the following pattern. It discourages tight looping of Database calls, which can severely limit performance. Still Bryan Hong's answer is the actual ANSWER to your question.
public IQueryable<EditSetting> GetEditSettings()
{
return from p in SettingRepository
select new EditSetting{
Foo = p.Foo,
Bar = p.Bar,
//etc...
};
}
or
public IEnumerable<EditSetting> GetEditSettingsById(IList<string> ids)
{
var ret = from p in SettingRepository
where ids.Contains(p.Id)
select new EditSetting{
Foo = p.Foo,
Bar = p.Bar,
//etc...
};
return ret.ToList();
}

Related

Anonymous type scoping issue

What is the proper way to create a variable that will house a list of anonymous objects that are generated through a LINQ query while keeping the variable declaration outside of a try/catch and the assignment being handled inside of a try/catch?
At the moment I'm declaring the variable as IEnumberable<object>, but this causes some issues down the road when I'm trying to use it later...
i.e.
var variableDeclaration;
try{
...
assignment
...
}catch...
EDIT:
If it's relevant (don't think it is) the list of objects is being returned as a Json result from an MVC3 action. I'm trying to reduce the time that some using statements are open with the DB as I'm having some performance issues that I'm trying to clear up a bit. In doing some of my testing I came across this issue and can't seem to find info on it.
EDIT 2:
If I could request the avoidance of focusing on LINQ. While LINQ is used the question is more specific to the scoping issues associated with Anonymous objects. Not the fact that LINQ is used (in this case) to generate them.
Also, a couple of answers have mentioned the use of dynamic while this will compile it doesn't allow for the usages that I'm needing later on the method. If what I'm wanting to do isn't possible then at the moment the answer appears to be to create a new class with the definition that I'm needing and to use that.
It's possible to get around this by creating a generic Cast method as outlined by Jon Skeet here. It will work and give you the intellisense you want. But, at this point, what's wrong with creating a custom type for your linq method?
public class MyClass
{
public int MyInt { get; set; }
}
IEnumerable<MyClass> myClass =
//Some Linq query that returns a collection of MyClass
Well, if you're using LINQ, the query is not evaluated unless materialized...
So, you might be able to:
var myQuery = //blah
try
{
myQuery = myQuery.ToList(); //or other materializing call
}
catch
{
}
Could you perhaps get away with using dynamic ??
dynamic variableDeclaration;
try
{
variableDeclaration = SomeList.Where(This => This == That);
}
catch { }
Not sure what this will affect further in your code block, but just a thought :)
If you are declaring the variable ahead of using it like a try/catch you can't use [var] as it is intendend. Instead you have to type the the variable.
var x = 0;
try{
x = SomethingReturningAnInt();
}
or
int x;
try{
x = SomethingReturningAnInt();
}
However in your case you don't really "know" what the method returns
var x = ...;
try{
x = Something();
}
catch{}
won't work
Option you have when you don't know the type in advance is use of dynamic:
dynamic x;
try{
x = Something();
}
catch{}
(But that feels like going back to VB4)
Another cheat: you can define variable locally (similarly to Jon's hack in Dave Zych answer) and than use it inside try/catch. As long as you can create the same anonymous item type before try-catch you are OK (as anonymous types wit the same field names and types are considered the same):
var myAnonymouslyType = Enumerable.Repeat(
new {Field1 = (int)1, Field2 = (string)"fake"}, 0);
try
{
myAnonymouslyType = ...(item =>
new {Field1 = item.Id, Field2=item.Text})...
}
...
This is safer option than covered in Jon's casting of anonymous types between functions because compiler will immediately find errors if types don't match.
Note: I'd vote for non-anonymous type if you have to go this way...
Note 2: depending on your actual need consider simply returning data from inside try/catch and having second return of default information outside.
This has vexed me for a while. In the end I've build some Generic helper methods where I can pass in the code that generates the anonymous objects, and the catch code as lamdas as follows
public static class TryCatch
{
public static T Expression<T>(Func<T> lamda, Action<Exception> onException)
{
try
{
return lamda();
}
catch(Exception e)
{
onException(e);
return default(T);
}
}
}
//and example
Exception throwexception = null;
var results = TryCatch.Expression(
//TRY
() =>
{
//simulate exception happening sometimes.
if (new Random().Next(3) == 2)
{
throw new Exception("test this");
}
//return an anonymous object
return new { a = 1, b = 2 };
} ,
//CATCH
(e) => { throwexception = e;
//retrow if you wish
//throw e;
}
);
https://gist.github.com/klumsy/6287279

Linq-to-sql logic pain

I've been trying to get the following method cleaned up using more sensible and lean syntax, but I'm striking serious headaches when it comes to aggregate clauses and filtering using L2S. Particularly, I feel I should be able to use a .Contains() method to filter out objects whose tags fit the string parameter passed in the method, but it hasn't worked.
public TagListViewModel GetTagModel(string Name)
{
var model = new TagListViewModel();
var repo = new SimpleRepository("Wishlist");
var ideas = repo.All<Idea>();
List<Idea> ideaList = new List<Idea>();
foreach (Idea i in ideas)
{
var query = from tag in repo.All<Tag>()
join ideatag in repo.All<IdeaTag>()
on tag.ID equals ideatag.TagId
where ideatag.IdeaId == i.ID
select tag;
i.Tags = query.ToList<Tag>();
ideaList.Add(i);
}
foreach (Idea i in ideaList)
{
var query = from vote in repo.All<IdeaVotes>()
where vote.IdeaId == i.ID
select vote;
i.Votes = query.ToList<IdeaVotes>();
}
// Here begins the problem area. I should be able to get a tag from the repo
// whose name matches the "Name" parameter and then call a .Contains() method to
// filter this list, shouldn't I?
List<Idea> filteredTagList = new List<Idea>();
foreach (Idea item in ideaList){
foreach(Tag t in item.Tags)
{
if (t.Name == Name)
filteredTagList.Add(item);
}
}
model.Ideas = filteredTagList;
return model;
}
It's ugly. I know it's ugly but after over 2 hours of playing with several preferred variations I still can't get it to filter the way it's supposed to. Where am I going wrong?
This should be equivalent assuming there are no duplicate tags on a single Idea.
model.Ideas = ideaList.Where(
idea => idea.Tags.Any(
tag => tag.Name == Name)).ToList();

How do I get values from SelectedItem in ComboBox with Linq and C# 3.5

I am really missing something with anonymous types, because I can't figure out what to do with the Combobox.SelectedItem property.
Here's the code that populates the combobox, and it works just fine
var stocks = from st in brdc.tb_dStocks
join su in brdc.tb_rStockUsers on st.StockID equals su.StockID
where su.UserID == userRec.UserID
select new { st.StockID, su.StockUserID, st.Ticker };
cboStocks.ItemsSource = stocks;
cboStocks.DisplayMemberPath = "Ticker";
Then, when someone selects an item using the cboStocks combobox I need to figure out what that item is, but I have no idea how to do it. Clearly, this is a simple problem, but its confusing me greatly. cboStocks.SelectedItem is an object, and that object is of the anonymous type created by Linq, but thats all I can figure out.
Anonymous types are only really useful (and should only be used) with a method. Here you're creating the type in one method when you initialise the combo box and then try and access it in another when reading the selected item. This isn't going to work.
You need to create an actual type to assign to the combo box's ItemsSource.
Unfortunately, there's no good way to do that without reflection. Anonymous types aren't really meant to be stashed and retrieved from later in absence of some big reflection framework to check them out. They're pretty much just designed for temporary convenience in methods that are rearranging data internally.
I suggest that you make a named type with the same three fields; then it's a trivial matter to cast it and get what you want back out.
Found the following approach on this blog a while ago, try the following:
private List<T> MakeList<T>(T itemOftype)
{
List<T> newList = new List<T>();
return newList;
}
//create a fake type for anonymous type
var stockType = new {StockID = 0, StockUserId =0, Ticker = string.Empty};
var listOfStocks = MakeList(stockType);
var listOfStocksAnonymous = from st in brdc.tb_dStocks
join su in brdc.tb_rStockUsers on st.StockID equals su.StockID
where su.UserID == userRec.UserID
select new { st.StockID, su.StockUserID, st.Ticker };
listOfStocks = listOfStocksAnonymous.ToList<stockType>();
//now you have a direct access to all anonymous properties
I agree with ChrisF. You should use a concrete type here. However this workaround works if you want to try it out:
T Cast<T>(object obj, T type)
{
return (T)obj;
}
...
var myItem = Cast(cboStocks.SelectedItem, new { st.StockID = 0, su.StockUserID = 0, st.Ticker = "" });
...
So here's what I ended up doing, seems to work pretty well
private class StockInfo
{
public int StockID { get; set; }
public int StockUserID { get; set; }
public string Ticker { get; set; }
public StockInfo(int stockID, int stockUserID, string ticker)
{
StockID = stockID;
StockUserID = stockUserID;
Ticker = ticker;
}
}
BaxRunDataContext brdc = new BaxRunDataContext();
IEnumerable<StockInfo> stocks = from st in brdc.tb_dStocks
join su in brdc.tb_rStockUsers on st.StockID equals su.StockID
where su.UserID == userRec.UserID
select new StockInfo(st.StockID, su.StockUserID, st.Ticker);
cboStocks.ItemsSource = stocks;
cboStocks.DisplayMemberPath = "Ticker";

LINQ - Add property to results

Is there a way to add a property to the objects of a Linq query result other than the following?
var query = from x in db.Courses
select new
{
x.OldProperty1,
x.OldProperty2,
x.OldProperty3,
NewProperty = true
};
I want to do this without listing out all of the current properties of my object. There are many properties, and I don't want to have to update this code whenever I may change my class.
I am still learning with LINQ and I appreciate your suggestions.
Add it with partial classes:
public partial class Courses
{
public String NewProperty { get; set; }
}
Then you can assign it after you've created the object.
I suppose you could return a new object composed of the new property and the selected object, like this:
var query = from x in db.Courses
select new
{
Course = x,
NewProperty = true
};
eking's answer will be the most straightforward approach.
If that doesn't work for you (because you need to pass the results around or whatever), and assuming the class you're dealing with already defines the property you want to set, you could create a copy constructor or factory method that takes an existing instance plus the value of the property you want to set:
var query = from x in db.Courses
select new Course(x, valueOfNewProperty);
Alternatively, if Course doesn't define the property, you could subclass it and use the same approach:
var query = from x in db.Courses
select new CourseWithExtraProperty(x, valueOfNewProperty);
(obviously, pick a better name for your subclass)
Again, though, unless you really need to do this, stick with eking's solution.
ServiceStack has a built-in way to handle this with the PopulateWith method.
Here's a code example.
foreach (var item in results)
{
var test1 = new ItemDto().PopulateWith(item);
test1.extraField1 = "extra";
response.Add(test1);
}`
And if you're not using ServiceStack, you can always use AutoMapper.
CreateMap<Foo, Bar>().ForMember(x => x.ExtraBarProperty, opt => opt.Ignore());
If you are looking to dynamically add a property to an object this could be a solution.
This is what has worked for me, I also had a concern and it was what happened with those domain objects that had many properties, the maintainability for any changes in the object was absurd, I managed to build an implementation with LINQ - ExpandObject - Reflection, which helped to keep my object dynamic and only add the additional properties that my view logic required.
var expandedModel = db.Courses.Select(x =>
{
dynamic expandObject = new ExpandoObject();
expandObject.NewProperty= $"PropertyValue";
foreach (var property in x.GetType().GetProperties())
{
((IDictionary<string, object>)expandObject).Add(property.Name, property.GetValue(x));
}
return expandObject;
}).ToList();

When to use an extension method with lambda over LINQtoObjects to filter a collection?

I am prototyping some C# 3 collection filters and came across this.
I have a collection of products:
public class MyProduct
{
public string Name { get; set; }
public Double Price { get; set; }
public string Description { get; set; }
}
var MyProducts = new List<MyProduct>
{
new MyProduct
{
Name = "Surfboard",
Price = 144.99,
Description = "Most important thing you will ever own."
},
new MyProduct
{
Name = "Leash",
Price = 29.28,
Description = "Keep important things close to you."
}
,
new MyProduct
{
Name = "Sun Screen",
Price = 15.88,
Description = "1000 SPF! Who Could ask for more?"
}
};
Now if I use LINQ to filter it works as expected:
var d = (from mp in MyProducts
where mp.Price < 50d
select mp);
And if I use the Where extension method combined with a Lambda the filter works as well:
var f = MyProducts.Where(mp => mp.Price < 50d).ToList();
Question: What is the difference, and why use one over the other?
LINQ turns into method calls like the code you have.
In other words, there should be no difference.
However, in your two pieces of code you are not calling .ToList in the first, so the first piece of code will produce an enumerable data source, but if you call .ToList on it, the two should be the same.
As mentioned d will be IEnumerable<MyProduct> while f is List<MyProduct>
The conversion is done by the C# compiler
var d =
from mp in MyProducts
where mp.Price < 50d
select mp;
Is converted to (before compilation to IL and with generics expanded):
var d =
MyProducts.
Where<MyProduct>( mp => mp.Price < 50d ).
Select<MyProduct>( mp => mp );
//note that this last select is optimised out if it makes no change
Note that in this simple case it makes little difference. Where Linq becomes really valuable is in much more complicated loops.
For instance this statement could include group-bys, orders and a few let statements and still be readable in Linq format when the equivalent .Method().Method.Method() would get complicated.
Other than the ToList difference, #2 is a lot more readable and natural IMO
The syntax you are using for d will get transformed by the compiler into the same IL as the extension methods. The "SQL-like" syntax is supposed to be a more natural way to represent a LINQ expression (although I personally prefer the extension methods). As has already been pointed out, the first example will return an IEnumerable result while the second example will return a List result due to the call to ToList(). If you remove the ToList() call in the second example, they will both return the same result as Where returns an IEnumerable result.

Categories

Resources