How to equate `var` variable to another query - c#

I have varvariable called retVal which equals to some query. After some conditions I want to equate it to another query. But I get an error like implicit cast of type "System.Collections.Generic.IEnumerable<AnonymousType#1>" in "System.Collections.Generic.IEnumerable<AnonymousType#2>" is impossible. You can ask me why I don't want to define another var variable. Because this one is used in foreach cycle. Let's have a look on the code:
var retVal = from groupItem in result.AsEnumerable()
where groupItem.Sms.First().amountOfParts == (
(from item in rawSmsList.AsEnumerable()
where item.referenceNumber == groupItem.Sms.First().referenceNumber
select item).Count()
)
select new
{
Value = groupItem.Value,
Sms = groupItem.Sms
};
//CONDITION
if (retVal.ToArray().Length==0)
{
//HERE I NEED TO RETVAL EQUATE NEW QUERY
retVal = from groupItem in result.AsEnumerable()
where groupItem.Sms.First().amountOfParts == (
(from item in rawSmsList.AsEnumerable()
where item.senderNumber == groupItem.Sms.First().senderNumber
select item).Count()
)
select new
{
Value = groupItem.Value,
Sms = groupItem.Sms
};
}
foreach (var item in retVal)//FOREACH EXPECTS THE SAME RETVAL!!!
So how to cast different queries to the same var variable? Or how to find type of var variable and then cast it to a new defined variable?

var means implicitly typed variable, that means its type will be determined at compile time, So on your first usage it will be assigned an anonymous type, in your second you are trying to assign it a different anonymous type, you can't do that.
You can modify your code to use a class instead of anonymous object and then project to that, then you will be to do what you are doing now. You can create the class like:
public class MyClass
{
public int Value {get;set;}
public string Sms {get;set;}
}
and then project it by modifying your select statement as:
var retVal = ......
select new MyClass
{
Value = groupItem.Value,
Sms = groupItem.Sms
};

The anonymous class you're using for your projections looks the same to us, but they're two separate classes as far as the compiler is concerned, which is why you'll see AnonymousType#1 and AnonymousType#2 in the error.
One solution is to simply select "groupItem" instead of projecting with an anonymous class since you're only using properties within the groupItem itself. This would give you the same IQueryable type.
Side note: you should replace "retVal.ToArray().Length == 0" with "!retVal.Any()"

To add to Habib's answer:
Just create an actual class instead of using the anonymous type (which is a different class every time you use it). I don't know the types from your example so you'll have to specify:
public class ValueSmsThing // Give it a better name!
{
public IDontKnow Value { get; private set; } // specify the type!
public SmsNumber Sms { get; private set; } // !!
public ValueSmsThing( IDontKnow value, SmsNumber sms) {
Value = value;
Sms = sms;
}
}
And then in your query, instead of using the anonymous type:
select new
{
Value = groupItem.Value,
Sms = groupItem.Sms
};
Use the concrete class you created
select new ValueSmsThing( groupItem.Value, groupItem.Sms );
Then your for loop will know to iterate over ValueSmsThings.

Related

Linq update issue with lambda

I am trying to write some code in Linq with lambda.This is my first code using lambda and i am facing an issue while updating Record.
My code is:
using (DataClasses1DataContext db = new DataClasses1DataContext())
{
Table<NOTIF_RECIP> NOTIF_RECIP_alias = db.GetTable<NOTIF_RECIP>();
Table<NOTIF_SCHED> NOTIF_SCHED_alias = db.GetTable<NOTIF_SCHED>();
Table<mainframe_replication> mainframe_replication_alias = db.GetTable<mainframe_replication>();
var ids = NOTIF_SCHED_alias.Select(x => x.NOTIF_RPT_ID).ToArray();
foreach (string notif_sched_data in ids)
{
var repljoinmf = mainframe_replication_alias
.Join(NOTIF_RECIP_alias,
mfr => mfr.RPT_ID,
nr => nr.NOTIF_RECIP_ID,
(mfr, nr) => new
{
ReportId=mfr.RPT_ID,
Reportversion=mfr.RPT_VERS,
ReportBytes= mfr.RPT_BYTES.ToString(),
ReportDate=mfr.REPL_DTM.ToString(),
NotifId= mfr.NOTIF_ID,
RecipAdd=nr.NOTIF_RECIP_ADDR
});
foreach(var repljoinmf_data in repljoinmf)
{
//DO STUFF
repljoinmf_data.NotifId = "Changedxyz";
//db.SubmitChanges();
}
}
}
I am getting Error in repljoinmf_data.NotifId = "Changedxyz";
Error says: Error 2 Property or indexer 'AnonymousType#3.NotifId' cannot be assigned to -- it is read only
Can someone please help me in this.I think it is because I am using var which is anonymous but how to solve the problem.Any help is appreciated.
Thanks
As the error suggests, anonymous class instances cannot be modified once they have been projected.
Although you could switch to a strong typed class, and then reassign the member properties, however, you have an opportunity to project the desired result in the preceding LINQ statement into the same anonymous class:
var repljoinmf = mainframe_replication_alias
.Join(NOTIF_RECIP_alias, mfr => mfr.RPT_ID, nr => nr.NOTIF_RECIP_ID,
(mfr, nr) => new // Anon Class projection
{
ReportId=mfr.RPT_ID,
Reportversion=mfr.RPT_VERS,
ReportBytes= mfr.RPT_BYTES.ToString(),
ReportDate=mfr.REPL_DTM.ToString(),
NotifId= "Changedxyz", // *** No need to mutate this afterwards
RecipAdd=nr.NOTIF_RECIP_ADDR
});
Edit, Update isn't trivial assignment, suggested alternatives
Option #1 : Strongly typed Class with mutation after projection
Add a new class (I've guessed some types)
public class MyPoco
{
public int ReportId {get; set;}
public string Reportversion {get; set;}
public byte[] ReportBytes {get; set;}
public DateTime ReportDate {get; set;}
public int NotifId {get; set;}
public string RecipAdd {get; set;}
}
Which you can then project into (just specify the class name instead of anonymous):
(mfr, nr) => new MyPoco // Not anonymous
{
ReportId=mfr.RPT_ID,
...
And then do modification afterwards:
foreach(var repljoinmf_data in repljoinmf)
{
repljoinmf_data.NotifId = "SomeNewValue"
Option #2 - Create a method (or Func) which does the complex logic
Since you seem to have already materialized all the data, you are free to use complex functions in the property projections. Any of the available local variables (closure) are available to pass to thus function, as are the join lambda parameters (mfr, nr)
So for example, write a function to calculate your NotifId = "Changedxyz" replacement:
private string DoIntensiveLogic(mainframe_replication mfr, NOTIF_RECIP nr)
{
// Do Stuff
}
Which you can then use in your original anonymous projection:
(mfr, nr) => new // Anon Class projection
{
ReportId=mfr.RPT_ID,
Reportversion=mfr.RPT_VERS,
ReportBytes= mfr.RPT_BYTES.ToString(),
ReportDate=mfr.REPL_DTM.ToString(),
NotifId= DoIntensiveLogic(mfr, nr), // Call the function each row
RecipAdd=nr.NOTIF_RECIP_ADDR
});
Anonymous types are immutable and hence created cannot be changed you have to create a new type.
To solve your issue you have to create your own type and avoid the use of anonymous type when a future update is needed.
your type may look like this
public class ReportInfo
{
public int Id{get; set;}
//the same thing for others properties
}
and your query will look like this
new ReportInfo() {
Id = mfr.RPT_ID,
Reportversion = mfr.RPT_VERS,
ReportBytes = mfr.RPT_BYTES.ToString(),
ReportDate = mfr.REPL_DTM.ToString(),
NotifId = mfr.NOTIF_ID,
RecipAdd = nr.NOTIF_RECIP_ADDR
})
than you can update easily your property
foreach(var repljoinmf_data in repljoinmf)
{
//DO STUFF
repljoinmf_data.NotifId = "Changedxyz";
//db.SubmitChanges();
}
More about anonymous Types
what the compiler is actually doing. When you write a line of code like this:
var o = new { property1 = expression1, ..., propertyN = expressionN };
the compiler infers the type of each expression, creates private fields of these inferred types, creates
public read-only properties for each of the fields, and creates a constructor that accepts all these
expressions. The constructor’s code initializes the private read-only fields from the expression results
passed in to it. In addition, the compiler overrides Object’s Equals, GetHashCode, and ToString
methods and generates code inside all these methods.
if you want to change 'NotifId' later, you can find a record by id and change the property.
Example:
var alias = mainframe_replication_alias.SingleOrDefault(mfr => mfr.NOTIF_ID == repljoinmf_data.NotifId);
if(alias != null)
alias.NOTIF_ID = "Changedxyz";

Insert an object in the list in C#

I have a list of class objects UserData. I get an object from this list through where method
UserData.Where(s => s.ID == IDKey).ToList(); //ID is unique
I would like to make some changes in the object and insert at the same location in the list. However, I donot have the index of this object.
Any idea how to do it ?
Thanks
You can get the index using the method
UserData.FindIndex(s => s.ID == IDKey)
It will return an int.
When your getting the item from a LIST its an reference type, if your updating anything to it then it will automatically change the values in LIST. Please check your self after updating...........
Item whichever your getting from
UserData.Where(s => s.ID == IDKey).ToList();
is an reference type.
As long as UserData is reference type, the list only holds references to instances of that object. So you can change its properties without the need of remove/insert (and obviously do not need index of that object).
I also suggest you want to use Single method (instead of ToList()) as long as the id is unique.
Example
public void ChangeUserName(List<UserData> users, int userId, string newName)
{
var user = users.Single(x=> x.UserId == userId);
user.Name = newName; // here you are changing the Name value of UserData objects, which is still part of the list
}
just fetch the object using SingleOrDefault and make related changes; you do not need to add it to the list again; you are simply changing the same instance which is an element of the list.
var temp = UserData.SingleOrDefault(s => s.ID == IDKey);
// apply changes
temp.X = someValue;
If I'm misunderstanding you then please correct me, but I think you're saying that you essentially want to iterate through the elements of a list, and if it matches a condition then you want to alter it in some way and add it to another list.
If that's the case, then please see the code below to see how to write an anonymous method using the Where clause. The Where clause just wants an anonymous function or delegate which matches the following:
parameters: ElementType element, int index -- return: bool result
which allows it to either select or ignore the element based upon the boolean return. This allows us to submit a simple boolean expression, or a more complex function which has additional steps, as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace StackOverflow
{
class Program
{
static void Main(string[] args)
{
int IDKey = 1;
List<SomeClass> UserData = new List<SomeClass>()
{
new SomeClass(),
new SomeClass(1),
new SomeClass(2)
};
//This operation actually works by evaluating the condition provided and adding the
//object s if the bool returned is true, but we can do other things too
UserData.Where(s => s.ID == IDKey).ToList();
//We can actually write an entire method inside the Where clause, like so:
List<SomeClass> filteredList = UserData.Where((s) => //Create a parameter for the func<SomeClass,bool> by using (varName)
{
bool theBooleanThatActuallyGetsReturnedInTheAboveVersion =
(s.ID == IDKey);
if (theBooleanThatActuallyGetsReturnedInTheAboveVersion) s.name = "Changed";
return theBooleanThatActuallyGetsReturnedInTheAboveVersion;
}
).ToList();
foreach (SomeClass item in filteredList)
{
Console.WriteLine(item.name);
}
}
}
class SomeClass
{
public int ID { get; set; }
public string name { get; set; }
public SomeClass(int id = 0, string name = "defaultName")
{
this.ID = id;
this.name = name;
}
}
}

Returning collection of anonymous types in C#

I have a VS2010 solution consisting of two projects - a "data" project and a webservice project that consumes the data project. In order to protect from exposing the schema of the database I've opted to return anonymous (var) objects to the consumer of the webservice. My question is, some of what I return will be in collections. So instead of using this code to return a single anonymous object:
var item = from w in db.Widgets
where w.widget_id == 1
select new {
name = w.name,
address = w.address
};
I would want to use something similar to this to return a collection.
IQueryable<var> item = (from w in db.Widgets
where w.widget_id == 1
select new {
name = w.name,
address = w.address
}).IQueryable;
I realize this is not the exact way to do this ... just need to know how it would really be done.
Thanks!
Anonymous types themselves cannot be exposed outside of their declaring function, so the only way to expose an instance of an anonymous type outside of the declaring function is as an object. If you need to do this, you'll have to declare a type at a higher scope with the properties that you need, then hydrate it within your query rather than the anonymous type.
Instead of returning anonymous types, I'd have a strong typed layer that is returned. It can still be filled from the database in whichever way you want and you'd have a strong contract for the consumer of the service. The consumer won't even know there is a database, it could just as well be xml.
You could, in theory, do this:
public List<object> GetObjects()
{
return (from w in db.Widgets
where w.widget_id == 1
select (object) new {
name = w.name,
address = w.address
}).ToList();
}
However, I don’t understand why you want to do this — the caller will obviously not be able to make any use of your anonymous type, except via Reflection or other hacky stuff. Why not simply return a normal class that everyone can use normally?
public class NameAndAddress
{
public string Name { get; private set; }
public string Address { get; private set; }
public NameAndAddress(string name, string address)
{
Name = name;
Address = address;
}
}
public List<NameAndAddress> GetObjects()
{
return (from w in db.Widgets
where w.widget_id == 1
select new NameAndAddress(w.name, w.address)
).ToList();
}

What type should I use with this aggregate query?

I am just getting my feet wet with LINQ to SQL, and need some advice in implementing a simple scenario:
I have a method ListTagCounts that is doing an aggregate query with LINQ To SQL. I was not sure of the return type to use, so used the ToList() method and created a class just for the return type in order to get it to compile.
Schema:
Link LinkTag Tag
---------- ---------- ----------
LinkID LinkID TagID
URL TagID Name
IsDeleted
Code:
static void Main(string[] args)
{
var counts = ListTagCounts();
foreach(var c in counts)
Console.WriteLine("Tag: {0}, Count: {1}", c.Tag, c.Count);
Console.ReadKey();
}
public static List<MyReturnClass> ListTagCounts()
{
List<MyReturnClass> counts;
using (var db = new MyDbDataContext())
{
counts = (from t in db.Tags
join lt in db.LinkTags on t.TagID equals lt.TagID
join l in db.Links on lt.LinkID equals l.LinkID
where l.IsDeleted == false
select new MyReturnClass() { Tag = t.Name, Count = t.LinkTags.Count() }
).Distinct().OrderBy(t => t.Tag).ToList();
}
return counts;
}
public class MyReturnClass
{
public string Tag { get; set; }
public int Count { get; set; }
}
If I do
select new { Tag = t.Name, Count = t.LinkTags.Count() }
rather than
select new MyReturnClass() { Tag = t.Name, Count = t.LinkTags.Count() }
in my LINQ query, what should the return type be?
When you use the following syntax:
select new { Tag = ..., Count = ... };
You are creating an anonymous type. The compiler generates the name of this type for you at compile-time, so there is no way for you to know what it is.
When you call ToList on the query that selects instances of this anonymous type, it's possible that you can return the list because List implements some non-generic classes (which obviously don't rely on the type parameter T, which you aren't aware of) you can cast to.
Generally though, you should not let anonymous types escape the method that they are used in. If you have the need to pass the information outside of the method that the query is in, then what you are doing is correct, in creating a specialized class and then returning a sequence of that.
Note there are some justified uses for passing anonymous types outside of your method (through a return type of object, or other base classes for sequences of these types), but for cases like this, it isn't one of them.
The first option will return an anonymous type which cannot be a return type of your method, the second way is a concrete class that holds your information, so the type is known and therefore is valid to be returned.
I would also suggest to return IEnumerable<MyReturnClass> unless you need it to be a list (read: you expect to add more items to it)
In general you'd want to assign that to the 'var' type, but that's only good for local variables. 'object' maybe??? But that's a longshot

Iterate through IQueryable of no specific type?

So I have this LINQ query that ends in a custom select kinda like this:
select new { this1 = table.this1, this2 = othertable.this2 }
The call to that query from the Controller looks something like this:
ViewData["these"] = theRepo.GetAllThese(someVar, anotherVar);
Now when I pass this on to my view since it is not strongly typed how can I iterate through it with a foreach, how can I cast it as an IQueryable or a List if I don't know what's in it?
...is it something like this?
IQueryable<???> these = ViewData["These"];
foreach (var this in these) {...
Just need to know what to put for '???' I think.
You cannot use an anonymous type (select new { Property = value }) outside the scope in which it is defined. So you should use foreach(var x in {yourQueryHere}) from within the method you defined the query in.
Example:
This is possible:
public void myMethod() {
var x = from c in IEnumerable select new { Prop = value };
foreach (var y in x) {
}
}
This is impossible:
public void myMethod() {
foreach (var y in myMethod2()) {
}
}
public IQueryable<???> myMethod2() {
return from c in IEnumerable select new { Prop = value };
}
Your linq query returns a collection of anonymously typed objects. Being anonymous, there is no way to "call their name" when declaring an explicitly typed variable. Thus, the true type/shape of the objects is only known within the action method where the objects are defined.
The indexed getter of the ViewData object has a return type of object, and without knowing the type name, you want be able to cast the return value of ViewData["these"] to anything useful.
What you might want to do instead, is to create a model - more specifically a "view model" - which defines the structure of the objects you are selecting using LINQ:
public class FoobarViewModel
{
public string Foo { get; set; }
public string Bar { get; set; }
}
and redefine your query to do a select like this:
select new FoobarViewModel { foo = table.this1, bar = othertable.this2 }
Your objects now share a common named class, and your collection can be easily casted to the proper type in the view.

Categories