Select single object from a query - c#

I have the following query that pulls the data I need perfectly fine.
var subFuncName = from a in m_dcSQL_ConnectionProdTest.DC3_SubFunctions
where a.VersionIndex == versionIndex && stepDistinct.Select(b => b.Step).Contains(a.FunctionNumber) && stepDistinct.Select(c => c.LogID).Contains(a.SubFunctionNumber)
select new
{
a.FunctionNumber,
a.SubFunctionNumber,
a.SubFunctionName,
};
Then I want to add some data to a list.
foreach (var item in stepDistinct)
{
lstPareto.Add(new clsPareto(Convert.ToInt32(item.Step), Convert.ToInt32(item.LogID),
stepLogID.Where(p => p.Step.Equals(item.Step) && p.LogID.Equals(item.LogID)).Count(),
subFuncName.Where(x => x.FunctionNumber.Equals(item.Step) && x.SubFunctionNumber.Equals(item.LogID)).Select(x => x.SubFunctionName).ToString())); --THIS LINE IS THE PROBLEM--
}
My clsPareto class:
public class clsPareto
{
public int intStep { get; set; }
public int intLogID { get; set; }
public int iCount { get; set; }
public string strFuncName { get; set; }
public clsPareto(int ParetoStep, int ParetoLogID, int Count, string FuncName)
{
intStep = ParetoStep;
intLogID = ParetoLogID;
iCount = Count;
strFuncName = FuncName;
}
}
What I am trying to do, is to pull each SubFunctionName from subFuncName where FunctionNumber = Step and SubFunctionNumber = LogID. However, when I bind it to my datagrid, the column meant to show the names just shows the SQL Query String instead and doesn't actually take the elements I want. I thought my .Select(x => x.SubFunctionName) would do the trick, but apparently it doesn't. Still pretty new to using LINQ and C#, so how would I go about doing this?

The linq for the problem line is still an expression - Select() returns an IEnumerable not the value - and then you are doing a ToString() on it. This is why you just get SQL back.
You need to resolve the expression and get an actual object out of it. Adding Single() to get the FuncName should do it. You may also not need to convert to string if FuncName already is:
subFuncName.Where(x => x.FunctionNumber.Equals(item.Step) && x.SubFunctionNumber.Equals(item.LogID))
.Select(x => x.SubFunctionName).Single().ToString()));

This solution will throw an exception if there is no matching element in the collection.
subFuncName.Where(x => x.FunctionNumber.Equals(item.Step) && x.SubFunctionNumber.Equals(item.LogID))
.Select(x => x.SubFunctionName).Single().ToString()));
Could use;
var subFuncName = subFuncName.Where(x => x.FunctionNumber.Equals(item.Step) && x.SubFunctionNumber.Equals(item.LogID))
.Select(x => x.SubFunctionName).FirstOrDefault());
if(subFuncName != null)
// Add it
else
subFuncName == "UNDEFINED";
and handle the case if subFuncName is null yourself.

Related

IEnumerable failed to set element

I have a ViewModel that contains different elements inside different tables that I tend to assign to it by query.
My problem is that I can't do this with IEnumerable (in GetAll() below), it keeps returning me null for RoomCode but for a single item (in GetDeviceId() below) then it works fine.
public IEnumerable<DeviceViewModel> GetAll()
{
var result = deviceRepository.GetAll().Select(x => x.ToViewModel<DeviceViewModel>());
for(int i = 0; i < result.Count(); i++)
{
int? deviceID = result.ElementAt(i).DeviceId;
result.ElementAt(i).RoomCode = deviceRepository.GetRoomCode(deviceID);
}
return result;
}
public DeviceViewModel GetDeviceID(int deviceID)
{
var result = new DeviceViewModel();
var device = deviceRepository.Find(deviceID);
if (device != null)
{
result = device.ToViewModel<DeviceViewModel>();
result.RoomCode = deviceRepository.GetRoomCode(deviceID);
}
else
{
throw new BaseException(ErrorMessages.DEVICE_LIST_EMPTY);
}
return result;
}
public string GetRoomCode(int? deviceID)
{
string roomCode;
var roomDevice = dbContext.Set<RoomDevice>().FirstOrDefault(x => x.DeviceId == deviceID && x.IsActive == true);
if (roomDevice != null)
{
var room = dbContext.Set<Room>().Find(roomDevice.RoomId);
roomCode = room.RoomCode;
}
else
{
roomCode = "";
}
return roomCode;
}
First, you need to materialize the query to a collection in local memory. Otherwise, the ElementAt(i) will query the db and give back some kind of temporary object each time it is used, discarding any change you do.
var result = deviceRepository.GetAll()
.Select(x => x.ToViewModel<DeviceViewModel>())
.ToList(); // this will materialize the query to a list in memory
// Now modifications of elements in the result IEnumerable will be persisted.
You can then go on with the rest of the code.
Second (and probably optional), I also recommend for clarity to use foreach to enumerate the elements. That's the C# idiomatic way to loop through an IEnumerable:
foreach (var element in result)
{
int? deviceID = element.DeviceId;
element.RoomCode = deviceRepository.GetRoomCode(deviceID);
}

Select Distinct rows when only one columns is different Linq

I have a data = List<Model>, where Model looks like this
public class Model{
public string String1 { get; set; }
public int Int1{ get; set; }
public int Int2{ get; set; }
public string String2 { get; set; }
public decimal Decimal1{ get; set; }
...
public decimal DecimalN{ get; set; }
}
I want to have average of each DecimalX values group by String1, Int1, Int2, but my problem is that sometimes I have two or more the same rows and only String2 is different, so I would like to do Distinct, but doesn't work because of this String2 property. I was trying to change all values of String2 to null or empty string
var x = data.ForEach(x => x.String2= null);
but I receive error Cannot asign void to implicitly- typed variable.
ForEach does not return anything - it's is void method, so your assignment to x is not valid.
Instead of this:
var x = data.ForEach(x => x.String2= null);
You should do like this:
data.ForEach(x => x.String2= null);
Just like Reniuz said that error occurs becaus forEach doesn't return anything (void).
Returning to your main problem, you mentioned that you need the average value for each Decimal.
For Decimal1 you can do something like this:
var b = list.GroupBy(g => new { g.String1, g.Int1, g.Int2 }).Select(r=> new {r.Key.String1, r.Key.Int1, r.Key.Int2, avgDecimal1 = r.Select(g=>g.Decimal1).Average()}).ToList();
First you need to Group by the elements that are going to be your keys (without String2 like you said) and after that, select those keys and the AVG of the elements of a determined property (e.g. Decimal1). You can add more AVG functions if you want (avgDecimal2, avgDecimal3, etc)
you want to start with projecting your results with the values you want to work with or in the way you want to work with them.
var query = source.Select(e => new
{
e.String1,
e.Int1,
e.Int2,
e.Decimal1,
});
Just omit the properties you don't want.
// or go straight to your average
var sum = data.GroupBy(e => new
{
e.String1,
e.Int1,
e.Int2,
e.Decimal1,
}).Average(e => e.Key.Decimal1);
I don't understand why you want distinct values. May be you can provide an example.
One solution can be -
var result = data.GroupBy(x => new { x.String1, x.Int1, x.Int2 })
.Select(g => new
{
String1 = g.Key.String1,
Int1 = g.Key.Int1,
Int2 = g.Key.Int2,
AvgDecimal1 = g.Select(x => x.Decimal1).Average(),
AvgDecimal2 = g.Select(x => x.Decimal1).Average(),
AvgDecimalN = g.Select(x => x.Decimal1).Average()
}).ToList();
If you want distinct values you can add distinct before group by -
data.Select(d => new Model { String1 = d.String1, Int1 = d.Int1, Int2 = d.Int2, Decimal1 = d.Decimal1, Decimal2 = d.Decimal2, DecimalN = d.DecimalN }) //returning new collection with String2 as null
.Distinct()
First solution produces following result -

Search for an object list

I have a class like this.
class man
{
public string name { get; set; }
public string mail { get; set; }
}
I have a list like this
List<man> ppl = new List<man>();
I want to search for a man with name "Nimal" in the list and delete that man. How to do that?
How about List.RemoveAll Method
Removes all the elements that match the conditions defined by the
specified predicate.
arts.RemoveAll(x => x.name == "Nimal");
if you want to delete the first found in list:
if(arts.Any(x => x.name == "Nimal"))
{
arts.Remove(arts.FirstOrDefault(x => x.name == "Nimal"));
}
if you want to delete all entries with matching condition then,you can do this way:
if(arts.Any(x => x.name == "Nimal"))
{
arts.RemoveAll(x => x.name == "Nimal");
}
var Filtered= ppl.FirstOrDefault(a => a.Name == "Nimal");
var finalresult= ppl.Except(Filtered).ToList();
If you a want the first occurrence of the name otherwise checkout #astander answer:
var firstMatch = ppl.First (p => p.Name == "Nimal");
ppl.Remove (firstMatch);
If you prefer for loop and if-condition you can do something like that:
for (int i = 0; i < ppl.Count; i++)
{
man m = ppl[i] as man;
if(m.Name.ToLower() == "nimal")
ppl.Remove(m);
}

Entity Framework throwing exception with Extension Method

I have the following code in my Controller:
public List<DockDoorViewModel> GetDoorViewModel()
{
List<DockDoorViewModel> doors = new List<DockDoorViewModel>();
for (int i = 1; i < 11; i++)
{
// This is where the Stack Trace is pointing to.
DockDoorViewModel door = db.vwDockDoorDatas
.Where(x => x.DockNo == i)
.Select(x => x.ToDockDoorViewModel())
.FirstOrDefault();
if (door == null)
{
door = new DockDoorViewModel(i);
}
else
{
door.Items = db.vwDockDoorDatas
.Where(x => x.DockNo == i)
.Select(x => x.ToDockDoorItem())
.ToList();
}
doors.Add(door);
}
return doors;
}
I am getting this exception when I try and Run the Web App:
Exception Details: System.NotSupportedException: LINQ to Entities does not recognize the method 'DockDoorMonitor.Models.DockDoorViewModel ToDockDoorViewModel(DockDoorMonitor.Models.vwDockDoorData)' method, and this method cannot be translated into a store expression.
Here is the extension method:
public static class vwDockDoorDataExtensions
{
public static DockDoorViewModel ToDockDoorViewModel(this vwDockDoorData x)
{
DockDoorViewModel vm = null;
if (x != null)
{
vm = new DockDoorViewModel()
{
ID = x.ID,
DockNo = x.DockNo,
loadType = x.loadType,
LoadDescription = x.LoadDescription,
Name = x.Name,
LocationCode = x.LocationCode,
SACode = x.SACode
};
}
return vm;
}
public static DockDoorItem ToDockDoorItem(this vwDockDoorData x)
{
DockDoorItem vm = null;
if (x != null)
{
vm = new DockDoorItem()
{
ID = x.ItemNo,
Description = x.Description,
Quantity = x.Quantity,
UnitOfMeasure = x.UnitOfMeasure
};
}
return vm;
}
}
I've done this kind of thing before so I'm not seeing what I am doing wrong? This is my first time with a MVC5 and EF6 application.
The error message tells you everything you need to know really - EF can't translate your extension methods to SQL therefore throws an exception. You need to convert your query from LINQ to Entities to LINQ to Objects, this can be done with a simple call to AsEnumerable() e.g.
DockDoorViewModel door = db.vwDockDoorDatas.Where(x => x.DockNo == i)
.AsEnumerable()
.Select(x => x.ToDockDoorViewModel())
.FirstOrDefault();
Effectively, what this does is create a hybrid query where everything before the AsEnumerable is translated and executed as SQL and the remainder being executed client-side and in memory.
As per your performance issues, looking at your query again you are unnecessarily pulling across a lot of records, you are only after the first one so why not just pull that one over i.e.
vwDockDoorData entity = db.vwDockDoorDatas.Where(x => x.DockNo == i)
.FirstOrDefault();
DockDoorViewModel door = entity != null ? entity.ToDockDoorViewModel() : null;
A further improvement on that would be to simply filter the records before you iterate them (give you have a start/end range) e.g.
var doorDatas = db.vwDockDoorDatas.Where(x => x.DockNo >= 1 && x.DockNo <= 11)
.ToList();
for (int i = 0; i < doorDatas.Count; i++)
{
// This is where the Stack Trace is pointing to.
DockDoorViewModel door = data.ToDockDoorViewModel();
if (door == null)
{
door = new DockDoorViewModel(i+1);
}
else
{
door.Items = data.ToDockDoorItem();
}
doors.Add(door);
}
The above would only require a single trip to the DB.
You will have to load the data from the SQL Server before using your to method. You can do this (for example) with the following command:
door.Items = db.vwDockDoorDatas
.Where(x => x.DockNo == i)
.ToList() //Possibly use AsEnumerable() here instead as James says
.Select(x => x.ToDockDoorItem())
.ToList();

entity framework Where method

i need some help in this , how do i write a where clause statement where i want to retrieve record based on 2 columns :
This is how i am trying to write in code :
public IList<Model.question> GetAll(string search , int search1)
{
IList<Model.question> lstQuestions = context.questions.ToList();
return lstQuestions.Where(a => a.TaskName.Contains(search) && p => p.ActivityID.Contains(search1)).ToList();
}
but there is an error with this statement .
----- ABOVE SOLVED ------------
Heres another problem :
public int GetMaxValue(string listTask , int listActivity)
{
int maxQNo = Convert.ToInt32(context.questions.Max(q => q.QuestionNo).Where(q.TaskName.Contains(listTask) && q.ActivityID == listActivity));
return maxQNo+1;
}
i get the error where q doesn't exist is current context , what i am trying to do here , is to get the max value of the column ( questionNo) where taskname = list task and activityid = list activity.
public IList<Model.question> GetAll(string search , int search1)
{
IList<Model.question> lstQuestions = context.questions.ToList();
return lstQuestions.Where(a => a.TaskName.Contains(search) && a.ActivityID==search1)).ToList();
}
Answer of new problem:
Add q => block in Where clause.
public int GetMaxValue(string listTask , int listActivity)
{
int maxQNo = Convert.ToInt32(context.questions.Max(q => q.QuestionNo)
.Where(q=>q.TaskName.Contains(listTask) &&
q.ActivityID.Contains(listActivity));
return maxQNo+1;
}

Categories