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);
}
Related
I have a situation where I'm passing an array of data (it can be any number, not fixed) from front-end, which is received as list in my API. I need each of the record's primary key value in order to pass them in my controller again to do further task. But using out, I am not able to return multiple data, it only returns the final value after finishing the loop.
Code-
public bool Add(List<Model> model) //have tried using out, but not working as I want
{
Model obj = new Model();
bool saved = false;
int id = 0;
foreach (Model m in model)
{
var data = _repo.Get(x => x.Id == m.Id);
var isExist = _repo.AsQueryable().Count();
if (isExist <= 0)
{
maxId = 1;
}
else
{
maxId = _repo.AsQueryable().Max(x => x.Id) + 1;
}
obj.Id = maxId; //the maxId for each loop iteration needs to be passed as out to fulfill my situation
obj.Name= m.Name;
obj.Dept= m.Dept;
_repo.Add(obj);
isSaved = true;
}
return isSaved;
}
Is there any way to return the maxId after each iteration or all maxId all together to my controller?
To return all at once create a List and either make that the return type of the method:
public List<TypeYouWantToReturn> Add(...)
{
List<TypeYouWantToReturn> list = new List<TypeYouWantToReturn>();
//make a loop and add the results here: list.Add(something);
return list;
}
or make it an out parameter as you mentioned:
public bool Add(out List<TypeYouWantToReturn> list)
{
//make a loop and add the results here: list.Add(something);
return true;
}
or use yield return to return values one by one.
So, I think I have a problem with restrictions, or at least missing something.
Basically I have a query that I execute with the following result set:
As you can see, there are 6 records.
Now inside my application it will return 6 records but it will add the order OR00221 multiple times, which is expected but what isn't expected is that it will only add the first one, with freightnr 6 multiple times.
As you can see, it add the 3rd record from the resultset for all items with this OrdNr OR00221.
I believe the problem is somewhere here:
restriction.Add(Restrictions.In(Projections.Property<ItemToPlan>(x => x.TrailerType), TrailerType));
The source of the class:
public static IList<ItemToPlan> GetItems(string[] TrailerType, Dictionary<string, string> filter, string orderBy, bool ascending, Dictionary<string, bool> groupingColumns)
{
if (TrailerType == null || TrailerType.Length == 0)
{
return new List<ItemToPlan>();
}
using (ISession session = NHibernateHelper.OpenSession())
{
var query = session.QueryOver<ItemToPlan>();
var restriction = Restrictions.Conjunction();
restriction.Add(Restrictions.In(Projections.Property<ItemToPlan>(x => x.TrailerType), TrailerType));
if (filter != null)
{
Type type = typeof(ItemToPlan);
IClassMetadata meta = session.SessionFactory.GetClassMetadata(type);
foreach (var item in filter)
{
if (!string.IsNullOrWhiteSpace(item.Value))
{
IType propertyType = meta.GetPropertyType(item.Key);
if (propertyType == NHibernateUtil.String)
{
restriction.Add(Restrictions.InsensitiveLike(Projections.Property(item.Key), item.Value, MatchMode.Anywhere));
}
else
{
restriction.Add(Restrictions.InsensitiveLike(Projections.Cast(NHibernateUtil.String, Projections.Property(item.Key)), item.Value, MatchMode.Anywhere));
}
}
}
}
if (restriction.ToString() != "()")
{
query.Where(restriction);
}
if (groupingColumns != null)
{
foreach (var item in groupingColumns)
{
if (item.Value)
{
query = query.OrderBy(Projections.Property(item.Key)).Asc;
}
else
{
query = query.OrderBy(Projections.Property(item.Key)).Desc;
}
}
}
else
{
groupingColumns = new Dictionary<string, bool>();
}
if (!string.IsNullOrWhiteSpace(orderBy) && !groupingColumns.ContainsKey(orderBy))
{
if (ascending)
{
query = query.OrderBy(Projections.Property(orderBy)).Asc;
}
else
{
query = query.OrderBy(Projections.Property(orderBy)).Desc;
}
}
var result = query.List<ItemToPlan>();
return result;
}
Source as pastebin: https://pastebin.com/aiTTBKBQ
You can use GroupBy to avoid duplication of records if this problem really exist in the query. I suppose that you have OrdNr property in your model.
Here is a sample of how to do this.
result = result.GroupBy(e => e.OrdNr).Select(e => e.FirstOrDefault());
I have two IEnumerable objects, if a specific condition is met, i need to take each element from first IEnumerable to second IEnumerable. My code is like this
IEnumerable<IPublishedContent> nodesTemp = posts.Take(10).ToList();
IEnumerable<IPublishedContent> nodes = null;
foreach (var n in nodesTemp)
{
if (condition=true)
{
nodes.add(n);
}
}
But this throws an error
: 'System.Collections.Generic.IEnumerable<Umbraco.Core.Models.IPublishedContent>' does not contain a definition for 'add' and no extension method 'add' accepting a first argument of type 'System.Collections.Generic.IEnumerable<Umbraco.Core.Models.IPublishedContent>' could be found (are you missing a using directive or an assembly reference?)
Complete code for reference
IEnumerable<IPublishedContent> nodesTemp = posts.Take(count).ToList();
IEnumerable<IPublishedContent> nodes = null;
foreach (IPublishedContent n in nodesTemp)
{
var rolename = n.GetProperty("focusedUserGroup").Value.ToString();
var username = umbraco.cms.businesslogic.member.Member.GetCurrentMember().Text;
var flag = false;
if (!string.IsNullOrEmpty(rolename))
{
var groups = rolename.Split(',');
foreach (var group in groups)
{
if (Roles.IsUserInRole(username, group))
{
nodes.add(n);
break;
}
}
}
}
You can't do this. The reason is because IEnumerable just represents iterator over some collection. It can be array in memory, select from remote database, or even constant call like this:
IEnumerable<int> GetSomeConsts()
{
yield return 1;
yield return 101;
yield return 22;
}
What you can do is to expand post iterator of your first collection. For example like this:
bool IsCondition(IPublishedContent n)
{
var rolename = n.GetProperty("focusedUserGroup").Value.ToString();
var username = umbraco.cms.businesslogic.member.Member.GetCurrentMember().Text;
if (!string.IsNullOrEmpty(rolename))
{
var groups = rolename.Split(',');
foreach (var group in groups)
{
if (Roles.IsUserInRole(username, group))
{
return true;
}
}
}
return false;
}
Then just call it like this:
var nodes = posts.Take(count).Where(IsCondition);
I think this is a great example of what you can use yield return for
eg:
public IEnumerable<IPublishedContent> Whatever(IEnmerable<IPublishedContent> nodes, int count)
{
foreach(var node in nodes.Take(count))
{
var rolename = node.GetProperty("focusedUserGroup").Value.ToString();
var username = umbraco.cms.businesslogic.member.Member.GetCurrentMember().Text;
if (!string.IsNullOrEmpty(rolename))
{
var groups = rolename.Split(',');
foreach (var group in groups)
{
if (Roles.IsUserInRole(username, group))
{
yield return node; //yield return allows you to create a new enumerable inline
break;
}
}
}
}
}
yield return will execute your code while you enumerate it, so if you were to call Whatever(...).First() it would only run through the code until it found the first yield return.
It looks like you're filtering an IEnumerable.
In such a case, you don't need to turn it into a list first, and you can use Enumerable<T>.Where() to filter it.
Something like this should work:
var nodesTemp = posts.Take(10);
var nodes = nodesTemp.Where(item => predicate(item));
Where predicate(item) is some function that takes a parameter of type IPublishedContent and returns a bool.
You can write that as a lambda expression, for example:
var nodesTemp = posts.Take(10);
var nodes = nodesTemp.Where(item => item.SomeProperty == someTargetValue);
If you need nodes to be a list rather than an IEnumerable<IPublishedContent>, you can turn it into a list:
var nodes = nodesTemp.Where(item => item.SomeProperty == someTargetValue).ToList();
At this point you probably don't really need nodesTemp (unless you're using it for debugging purposes):
var nodes =
posts.Take(10)
.Where(item => item.SomeProperty == someTargetValue)
.ToList();
I know I can get a string from resources using
Resources.GetIdentifier(token, "string", ctx.ApplicationContext.PackageName)
(sorry, this is in C#, it's part of a Xamarin.Android project).
I know that if my elements are called foo_1, foo_2, foo_3, then I can iterate and grab the strings using something like
var myList = new List<string>();
for(var i = 0; i < 4; ++i)
{
var id = AppContent.GetIdentifier(token + i.ToString(), "string", "package_name");
if (id != 0)
myList.Add(AppContext.GetString(id));
}
My issue is that my token names all begin with "posn." (the posn can denote the position of anything, so you can have "posn.left_arm" and "posn.brokenose"). I want to be able to add to the list of posn elements, so I can't really store a list of the parts after the period. I can't use a string-array for this either (specific reason means I can't do this).
Is there a way that I can use something akin to "posn.*" in the getidentifer call to return the ids?
You can use some reflection foo to get what you want. It is not pretty at all but it works. The reflection stuff is based on https://gist.github.com/atsushieno/4e66da6e492dfb6c1dd0
private List<string> _stringNames;
private IEnumerable<int> GetIdentifiers(string contains)
{
if (_stringNames == null)
{
var eass = Assembly.GetExecutingAssembly();
Func<Assembly, Type> f = ass =>
ass.GetCustomAttributes(typeof(ResourceDesignerAttribute), true)
.OfType<ResourceDesignerAttribute>()
.Where(ca => ca.IsApplication)
.Select(ca => ass.GetType(ca.FullName))
.FirstOrDefault(ty => ty != null);
var t = f(eass) ??
AppDomain.CurrentDomain.GetAssemblies().Select(ass => f(ass)).FirstOrDefault(ty => ty != null);
if (t != null)
{
var strings = t.GetNestedTypes().FirstOrDefault(n => n.Name == "String");
if (strings != null)
{
var fields = strings.GetFields();
_stringNames = new List<string>();
foreach (var field in fields)
{
_stringNames.Add(field.Name);
}
}
}
}
if (_stringNames != null)
{
var names = _stringNames.Where(s => s.Contains(contains));
foreach (var name in names)
{
yield return Resources.GetIdentifier(name, "string", ComponentName.PackageName);
}
}
}
Then somewhere in your Activity you could do:
var ids = GetIdentifiers("action").ToList();
That will give you all the String Resources, which contain the string action.
There is a sample application called MVCCrud. This example is quite good and I would like to use it as the framework on a project that I am working on.
The problem is that MVCCrud uses LingToSQL and I would like to use LinqToEntities. I got most everything to work correctly once I converted over to LinqToEntities except one place.
In the following code on the lines i = typeof(TModel).GetProperty(primaryKey).GetValue(p, null),
cell = getCells(p)
it gives a Linq to Entities does not recognize GetValue.
Can someone help me refactor the following code?
items = items.OrderBy(string.Format("{0} {1}", sidx, sord)).Skip(pageIndex * pageSize).Take(pageSize).AsQueryable();
// Generate JSON
var jsonData =
new
{
total = totalPages,
page,
records = totalRecords,
rows = items.Select(
p => new
{
// id column from repository
i = typeof(TModel).GetProperty(primaryKey).GetValue(p, null),
cell = getCells(p)
}).ToArray()
};
return Json(jsonData);
and here is the getCell method:
private string[] getCells(TModel p)
{
List<string> result = new List<string>();
string a = actionCell(p);
if (a != null)
{
result.Add(a);
}
foreach (string column in data_rows.Select(r => r.value))
{
try
{
// hack for tblcategory.name
string[] parts = column.Split('.');
// Set first part
PropertyInfo c = typeof(TModel).GetProperty(parts[0]);
object tmp = c.GetValue(p, null);
// loop through if there is more than one depth to the . eg tblCategory.name
for (int j = 1; j < parts.Length; j++)
{
c = tmp.GetType().GetProperty(parts[j]);
tmp = c.GetValue(tmp, null);
}
if (tmp.GetType() == typeof(DateTime))
{
result.Add(((DateTime)tmp).ToString(dateTimeFormat));
}
else if (tmp.GetType() == typeof(float))
{
result.Add(((float)tmp).ToString(decimalFormat));
}
else if (tmp.GetType() == typeof(double))
{
result.Add(((double)tmp).ToString(decimalFormat));
}
else if (tmp.GetType() == typeof(decimal))
{
result.Add(((decimal)tmp).ToString(decimalFormat));
}
else
{
result.Add(tmp.ToString());
}
}
catch (Exception)
{
result.Add(string.Empty);
}
}
return result.ToArray();
}
Do this ToList() instead of AsQueryable():
items = items.OrderBy(string.Format("{0} {1}", sidx, sord)).Skip(pageIndex * pageSize).Take(pageSize).ToList();
You can't execute any external method "within" linq query.
And may you say that was working in Linq2Sql then you should know when you call any external method "Like ToString()" Linq2Sql will fetch all data from database then handle your query in the memory and that maybe a serious harming if you have a lot of records.
For more information look at this