Converting string to int, array of strings - asp.net-mvc-3

I am having an issue with converting a string of id to an int when there are multiple strings passed in, the code shows the following:
for(int i = 0; i < Request.Params["service"].Length; i++)
{
int serviceID = int.Parse(Request.Params["service"]);
db.ServiceAssignments.Add(serviceAssignment);
serviceAssignment.locationID = locationID;
serviceAssignment.ServiceID = serviceID;
db.SaveChanges();
}
If you pass in one param, you get: int.Parse(Request.Params["Service"]); = 1, which then works and the database saves. however if you have the following you get:
'1,2' which three. What I want is 1 and then 2, not 1,2.
What is 1 and 2?
When you create anew location you get to select services for that location. The service id, in the case of this problem is 1 and 2. if I select one service then it saves and all is well. When I select two or more it doesnt work.
I though I could do:
Request.Params["Service"][i] because "Service" is an array after all. How ever this causes database problems and a whole other mess.
So what would you suggest I can do to make it save id 1 and id 2 when you select them for a location?

MVC 3 is quite powerful to figure out the binding, I don't know exactly what are you doing in the view that get the service Ids from user but I assume there is form there and if all Ids are int you can do like this and you don't need any conversion, or maybe you can use FormCollection. I don't think using Request in MVC 3 is a good idea, it does not really belong the whole MVC idea.
public void Add(int[] service)
{
foreach (var item in service)
{
int serviceID = item;
}
}
OR
public void Add(FormCollection frm)
{
foreach (var item in frm.AllKeys)
{
if (item.StartsWith("service"))
{
int serviceID = Int32.Parse(frm[item]);
}
}
}
anyway none of these are also MVC, these are should work but I recommend you to use Models in views and controllers

This will work. Just tested it:
string[] items = Request.Params["service"].Split(',');
for (int i = 0; i < items.Length; i++)
{
int serviceID = int.Parse(items[i]);
db.ServiceAssignments.Add(serviceAssignment);
serviceAssignment.locationID = locationID;
serviceAssignment.ServiceID = serviceID;
db.SaveChanges();
}
As a side note, I'd probably make two changes:
I'd use a foreach statement. No real difference; just less typing.
I'd put the SaveChanges() AFTER the for loop. This will make fewer calls to the database, but still accomplish the same thing.

Related

Spring Reactor - consuming Pageable endpoint

It's my first time working with Spring Reactor and I've faced the following challenge:
I have a service which allows to consume a number of records specified by page number and page size:
Mono<GetContactsForGroupResponse> getContactsForGroup(Integer page, Integer size);
Among other fields GetContactsForGroupResponse contains pagination metadata:
class GetContactsForGroupResponse {
private int totalPages;
private int totalElements;
private int numberOfElements;
private int size;
private int number;
private boolean first;
private boolean last;
//.....
}
Now I need to write another method that would read all of the pages from
Mono<GetContactsForGroupResponse> getContactsForGroup(Integer page, Integer size);
and combine results into one single collection:
Mono<Collection<GetContactsForGroupResponse>> getContactsForGroup();
So far I've written:
List<GetContactsForGroupResponse> groupContacts = new ArrayList<>();
AtomicBoolean allPagesConsumed = new AtomicBoolean(false);
int pageNumber = 0;
int pageSize = 10;
while(!allPagesConsumed.get()) {
allPagesConsumed.set(true);
GetContactsForGroupResponse getContactsForGroupResponse =
getContactsForGroup(accountId, g.getId(), 0, pageSize).block();
Optional.ofNullable(getContactsForGroupResponse)
.ifPresent(r -> {
allPagesConsumed.set(r.isLast());
groupContacts.add(r);
});
pageNumber ++;
I read results page by page until I reach the last page.
I wonder what is the right way of implementation from SpringReactor point of view
Any advice would be appreciated,
Thanks
there is no "right way", because this not a reactive question.
Since you are fetching "pages" you are dealing with a non reactive way of handling data. You have not disclosed anything about how you fetch this data, and from what type of database.
The easiest thing is to just make a query to the database and fetch everything in one go.
write a getAllContactsForGroup instead, and don't, do a while loop.

Scalable Contains method for LINQ against a SQL backend

I'm looking for an elegant way to execute a Contains() statement in a scalable way. Please allow me to give some background before I come to the actual question.
The IN statement
In Entity Framework and LINQ to SQL the Contains statement is translated as a SQL IN statement. For instance, from this statement:
var ids = Enumerable.Range(1,10);
var courses = Courses.Where(c => ids.Contains(c.CourseID)).ToList();
Entity Framework will generate
SELECT
[Extent1].[CourseID] AS [CourseID],
[Extent1].[Title] AS [Title],
[Extent1].[Credits] AS [Credits],
[Extent1].[DepartmentID] AS [DepartmentID]
FROM [dbo].[Course] AS [Extent1]
WHERE [Extent1].[CourseID] IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Unfortunately, the In statement is not scalable. As per MSDN:
Including an extremely large number of values (many thousands) in an IN clause can consume resources and return errors 8623 or 8632
which has to do with running out of resources or exceeding expression limits.
But before these errors occur, the IN statement becomes increasingly slow with growing numbers of items. I can't find documentation about its growth rate, but it performs well up to a few thousands of items, but beyond that it gets dramatically slow. (Based on SQL Server experiences).
Scalable
We can't always avoid this statement. A JOIN with the source data in stead would generally perform much better, but that's only possible when the source data is in the same context. Here I'm dealing with data coming from a client in a disconnected scenario. So I have been looking for a scalable solution. A satisfactory approach turned out to be cutting the operation into chunks:
var courses = ids.ToChunks(1000)
.Select(chunk => Courses.Where(c => chunk.Contains(c.CourseID)))
.SelectMany(x => x).ToList();
(where ToChunks is this little extension method).
This executes the query in chunks of 1000 that all perform well enough. With e.g. 5000 items, 5 queries will run that together are likely to be faster than one query with 5000 items.
But not DRY
But of course I don't want to scatter this construct all over my code. I am looking for an extension method by which any IQueryable<T> can be transformed into a chunky executing statement. Ideally something like this:
var courses = Courses.Where(c => ids.Contains(c.CourseID))
.AsChunky(1000)
.ToList();
But maybe this
var courses = Courses.ChunkyContains(c => c.CourseID, ids, 1000)
.ToList();
I've given the latter solution a first shot:
public static IEnumerable<TEntity> ChunkyContains<TEntity, TContains>(
this IQueryable<TEntity> query,
Expression<Func<TEntity,TContains>> match,
IEnumerable<TContains> containList,
int chunkSize = 500)
{
return containList.ToChunks(chunkSize)
.Select (chunk => query.Where(x => chunk.Contains(match)))
.SelectMany(x => x);
}
Obviously, the part x => chunk.Contains(match) doesn't compile. But I don't know how to manipulate the match expression into a Contains expression.
Maybe someone can help me make this solution work. And of course I'm open to other approaches to make this statement scalable.
I’ve solved this problem with a little different approach a view month ago. Maybe it’s a good solution for you too.
I didn’t want my solution to change the query itself. So a ids.ChunkContains(p.Id) or a special WhereContains method was unfeasible. Also should the solution be able to combine a Contains with another filter as well as using the same collection multiple times.
db.TestEntities.Where(p => (ids.Contains(p.Id) || ids.Contains(p.ParentId)) && p.Name.StartsWith("Test"))
So I tried to encapsulate the logic in a special ToList method that could rewrite the Expression for a specified collection to be queried in chunks.
var ids = Enumerable.Range(1, 11);
var result = db.TestEntities.Where(p => Ids.Contains(p.Id) && p.Name.StartsWith ("Test"))
.ToChunkedList(ids,4);
To rewrite the expression tree I discovered all Contains Method calls from local collections in the query with a view helping classes.
private class ContainsExpression
{
public ContainsExpression(MethodCallExpression methodCall)
{
this.MethodCall = methodCall;
}
public MethodCallExpression MethodCall { get; private set; }
public object GetValue()
{
var parent = MethodCall.Object ?? MethodCall.Arguments.FirstOrDefault();
return Expression.Lambda<Func<object>>(parent).Compile()();
}
public bool IsLocalList()
{
Expression parent = MethodCall.Object ?? MethodCall.Arguments.FirstOrDefault();
while (parent != null) {
if (parent is ConstantExpression)
return true;
var member = parent as MemberExpression;
if (member != null) {
parent = member.Expression;
} else {
parent = null;
}
}
return false;
}
}
private class FindExpressionVisitor<T> : ExpressionVisitor where T : Expression
{
public List<T> FoundItems { get; private set; }
public FindExpressionVisitor()
{
this.FoundItems = new List<T>();
}
public override Expression Visit(Expression node)
{
var found = node as T;
if (found != null) {
this.FoundItems.Add(found);
}
return base.Visit(node);
}
}
public static List<T> ToChunkedList<T, TValue>(this IQueryable<T> query, IEnumerable<TValue> list, int chunkSize)
{
var finder = new FindExpressionVisitor<MethodCallExpression>();
finder.Visit(query.Expression);
var methodCalls = finder.FoundItems.Where(p => p.Method.Name == "Contains").Select(p => new ContainsExpression(p)).Where(p => p.IsLocalList()).ToList();
var localLists = methodCalls.Where(p => p.GetValue() == list).ToList();
If the local collection passed in the ToChunkedList method was found in the query expression, I replace the Contains call to the original list with a new call to a temporary list containing the ids for one batch.
if (localLists.Any()) {
var result = new List<T>();
var valueList = new List<TValue>();
var containsMethod = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Single(p => p.Name == "Contains" && p.GetParameters().Count() == 2)
.MakeGenericMethod(typeof(TValue));
var queryExpression = query.Expression;
foreach (var item in localLists) {
var parameter = new List<Expression>();
parameter.Add(Expression.Constant(valueList));
if (item.MethodCall.Object == null) {
parameter.AddRange(item.MethodCall.Arguments.Skip(1));
} else {
parameter.AddRange(item.MethodCall.Arguments);
}
var call = Expression.Call(containsMethod, parameter.ToArray());
var replacer = new ExpressionReplacer(item.MethodCall,call);
queryExpression = replacer.Visit(queryExpression);
}
var chunkQuery = query.Provider.CreateQuery<T>(queryExpression);
for (int i = 0; i < Math.Ceiling((decimal)list.Count() / chunkSize); i++) {
valueList.Clear();
valueList.AddRange(list.Skip(i * chunkSize).Take(chunkSize));
result.AddRange(chunkQuery.ToList());
}
return result;
}
// if the collection was not found return query.ToList()
return query.ToList();
Expression Replacer:
private class ExpressionReplacer : ExpressionVisitor {
private Expression find, replace;
public ExpressionReplacer(Expression find, Expression replace)
{
this.find = find;
this.replace = replace;
}
public override Expression Visit(Expression node)
{
if (node == this.find)
return this.replace;
return base.Visit(node);
}
}
Please allow me to provide an alternative to the Chunky approach.
The technique involving Contains in your predicate works well for:
A constant list of values (no volatile).
A small list of values.
Contains will do great if your local data has those two characteristics because these small set of values will be hardcoded in the final SQL query.
The problem begins when your list of values has entropy (non-constant). As of this writing, Entity Framework (Classic and Core) do not try to parameterize these values in any way, this forces SQL Server to generate a query plan every time it sees a new combination of values in your query. This operation is expensive and gets aggravated by the overall complexity of your query (e.g. many tables, a lot of values in the list, etc.).
The Chunky approach still suffers from this SQL Server query plan cache pollution problem, because it does not parametrizes the query, it just moves the cost of creating a big execution plan into smaller ones that are more easy to compute (and discard) by SQL Server, furthermore, every chunk adds an additional round-trip to the database, which increases the time needed to resolve the query.
An Efficient Solution for EF Core
🎉 NEW! QueryableValues EF6 Edition has arrived!
For EF Core keep reading below.
Wouldn't it be nice to have a way of composing local data in your query in a way that's SQL Server friendly? Enter QueryableValues.
I designed this library with these two main goals:
It MUST solve the SQL Server's query plan cache pollution problem ✅
It MUST be fast! ⚡
It has a flexible API that allows you to compose local data provided by an IEnumerable<T> and you get back an IQueryable<T>; just use it as if it were another entity of your DbContext (really), e.g.:
// Sample values.
IEnumerable<int> values = Enumerable.Range(1, 1000);
// Using a Join (query syntax).
var query1 =
from e in dbContext.MyEntities
join v in dbContext.AsQueryableValues(values) on e.Id equals v
select new
{
e.Id,
e.Name
};
// Using Contains (method syntax)
var query2 = dbContext.MyEntities
.Where(e => dbContext.AsQueryableValues(values).Contains(e.Id))
.Select(e => new
{
e.Id,
e.Name
});
You can also compose complex types!
It goes without saying that the provided IEnumerable<T> is only enumerated at the time that your query is materialized (not before), preserving the same behavior of EF Core in this regard.
How Does It Works?
Internally QueryableValues creates a parameterized query and provides your values in a serialized format that is natively understood by SQL Server. This allows your query to be resolved with a single round-trip to the database and avoids creating a new query plan on subsequent executions due to the parameterized nature of it.
Useful Links
Nuget Package
GitHub Repository
Benchmarks
SQL Server Cache Pollution Problem
QueryableValues is distributed under the MIT license
Linqkit to the rescue! Might be a better way that does it directly, but this seems to work fine and makes it pretty clear what's being done. The addition being AsExpandable(), which lets you use the Invoke extension.
using LinqKit;
public static IEnumerable<TEntity> ChunkyContains<TEntity, TContains>(
this IQueryable<TEntity> query,
Expression<Func<TEntity,TContains>> match,
IEnumerable<TContains> containList,
int chunkSize = 500)
{
return containList
.ToChunks(chunkSize)
.Select (chunk => query.AsExpandable()
.Where(x => chunk.Contains(match.Invoke(x))))
.SelectMany(x => x);
}
You might also want to do this:
containsList.Distinct()
.ToChunks(chunkSize)
...or something similar so you don't get duplicate results if something this occurs:
query.ChunkyContains(x => x.Id, new List<int> { 1, 1 }, 1);
Another way would be to build the predicate this way (of course, some parts should be improved, just giving the idea).
public static Expression<Func<TEntity, bool>> ContainsPredicate<TEntity, TContains>(this IEnumerable<TContains> chunk, Expression<Func<TEntity, TContains>> match)
{
return Expression.Lambda<Func<TEntity, bool>>(Expression.Call(
typeof (Enumerable),
"Contains",
new[]
{
typeof (TContains)
},
Expression.Constant(chunk, typeof(IEnumerable<TContains>)), match.Body),
match.Parameters);
}
which you could call in your ChunkContains method
return containList.ToChunks(chunkSize)
.Select(chunk => query.Where(ContainsPredicate(chunk, match)))
.SelectMany(x => x);
Using a stored procedure with a table valued parameter could also work well. You in effect write a joint In the stored procedure between your table / view and the table valued parameter.
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql/table-valued-parameters

DropDown list with translated values

I have enums in my domain like this
public enum AdTypeEnum { Sale = 1, Rent = 2, SaleOrRent = 3 };
In my view I'm rendering this enums like this
<div class="editor-field">
#Html.DropDownListFor(x => x.AdType, new SelectList(Enum.GetValues(typeof(MyDomain.Domain.Property.AdTypeEnum))))
</div>
Now, what I'm trying to achive is based on selected language I want to display these enums in drop down list in selected language.
Did you try something like:
public static Array Localize(Array values)
{
string[] result = new string[values.Length];
for (int i = 0; i < values.Length; ++i)
result[i] = Resources.ResourceManager.GetString(values.GetValue(i).ToString());
return result;
}
A better implementation should use a different key-set for each enum type (using the enum name as part of the GetString() parameter).
Anyway this method is pretty naive, I think for a big application (or for more complex scenarios) you may need to inherit from SelectList class.

Linq2SQL "Local sequence cannot be used in LINQ to SQL" error

I have a piece of code which combines an in-memory list with some data held in a database. This works just fine in my unit tests (using a mocked Linq2SqlRepository which uses List).
public IRepository<OrderItem> orderItems { get; set; }
private List<OrderHeld> _releasedOrders = null;
private List<OrderHeld> releasedOrders
{
get
{
if (_releasedOrders == null)
{
_releasedOrders = new List<nOrderHeld>();
}
return _releasedOrders;
}
}
.....
public int GetReleasedCount(OrderItem orderItem)
{
int? total =
(
from item in orderItems.All
join releasedOrder in releasedOrders
on item.OrderID equals releasedOrder.OrderID
where item.ProductID == orderItem.ProductID
select new
{
item.Quantity,
}
).Sum(x => (int?)x.Quantity);
return total.HasValue ? total.Value : 0;
}
I am getting an error I don't really understand when I run it against a database.
Exception information:
Exception type: System.NotSupportedException
Exception message: Local sequence cannot be used in LINQ to SQL
implementation of query operators
except the Contains() operator.
What am I doing wrong?
I'm guessing it's to do with the fact that orderItems is on the database and releasedItems is in memory.
EDIT
I have changed my code based on the answers given (thanks all)
public int GetReleasedCount(OrderItem orderItem)
{
var releasedOrderIDs = releasedOrders.Select(x => x.OrderID);
int? total =
(
from item in orderItems.All
where releasedOrderIDs.Contains(item.OrderID)
&& item.ProductID == orderItem.ProductID
select new
{
item.Quantity,
}
).Sum(x => (int?)x.Quantity);
return total.HasValue ? total.Value : 0;
}
I'm guessing it's to do with the fact
that orderItems is on the database
and releasedItems is in memory.
You are correct, you can't join a table to a List using LINQ.
Take a look at this link:
http://flatlinerdoa.spaces.live.com/Blog/cns!17124D03A9A052B0!455.entry
He suggests using the Contains() method but you'll have to play around with it to see if it will work for your needs.
It looks like you need to formulate the db query first, because it can't create the correct SQL representation of the expression tree for objects that are in memory. It might be down to the join, so is it possible to get a value from the in-memory query that can be used as a simple primitive? For example using Contains() as the error suggests.
You unit tests work because your comparing a memory list to a memory list.
For memory list to database, you will either need to use the memoryVariable.Contains(...) or make the db call first and return a list(), so you can compare memory list to memory list as before. The 2nd option would return too much data, so your forced down the Contains() route.
public int GetReleasedCount(OrderItem orderItem)
{
int? total =
(
from item in orderItems.All
where item.ProductID == orderItem.ProductID
&& releasedOrders.Contains(item.OrderID)
select new
{
item.Quantity,
}
).Sum(x => (int?)x.Quantity);
return total.HasValue ? total.Value : 0;
}

Using LINQ query on Dictionary and List

I have a Dictionary<int, int> idsAndTypes = new Dictionary<int, int>(); and i have a
List<Product> products = new List<Product>()
as list of products , the product class is as below
class Product
{
public int Id {get;set;}
public int Type{get;set;}
}
the dictionary idsAndTypes contains id's and types , now i want to use a linq query on the list to update the type of products based on id's in the dictionary....
i know the other way can be like following :
foreach (int item in idsAndTypes.Keys)
{
Product.Where(product => product.Id == item).
Select(product => product).ToList()[0].
Type = idsAndTypes[item];
}
but i want to do it with a linq query to avoid the foreach loop, is there a way to do it ?
Please suggest...
Well, LINQ is really for querying not for updating.
There may be a way of doing it without the loop, but I think the loop is the cleanest way. However, I wouldn't use that loop. You've got something which is very quick to look up, and you're just iterating through it... but then doing a lookup (effectively) on a slow data structure in terms of lookup. I'd do this:
foreach (Product p in products)
{
int type;
if (idsAndTypes.TryGetValue(product.Id, out type))
{
p.LinkedProductType = type;
}
}
One difference here - that will update all the products in the list with values in the dictionary; your current code will only do the first product in the list with the given ID. I hope that isn't a problem.
Your sample code is quite confusing. But I think what you want is:
products = products.Select(p =>
{
p.LinkedProductType = idAndTypes[p.ID];
return p;
}
);
While this should achieve the goal, I would considered it an abuse of LINQ.

Resources