Building LINQ query from parameters - asp.net

Consider a simple Employee class.
Class Employee
{
public String FirstName {get; set;}
public int Id {get; set;}
public int Marks {get; set;}
public String LastName {get; set;}
}
So for selecting by LINQ query we can write.
var query = Employee.Where(i => i.Id > 2).OrderBy(i => i.Marks);
So can we create a function which takes 2 parameter where we can send Id and Marks.
So that i can make a function call and pass parameter what ever i need like.
var query = Employee.Where(i => i.Marks > 2).OrderBy(i => i.FirstName);
Sample looking function where we can pass any parameter
Public String GetQuery(String para1,String para2,......)
{
var query = Employee.Where(i => i.para1 > 2).OrderBy(i => i.para2);
return query;
}
or
Public String GetQuery(String para1,String para2,......)
{
String str1=para1...... // with some format included
String str2=para2...... // with some format included
var query = Employee.Where(str1).OrderBy(str2);
return query;
}
The concept is that i want to create a common(generic) query in which i can select values
of any type of parameter passed.

A type safe solution could use delegates instead of strings:
IEnumerable<Employee> employees = getExampleData();
IEnumerable<Employee> example1 = Query.Employees(employees, Query.UseID, Query.UseMarks);
IEnumerable<Employee> example2 = Query.Employees(employees, Query.UseMarks, Query.UseFirstName);
I made a helper class Query to wrap the functionality:
static class Query
{
public static int UseID(Employee employee, int i) { return employee.Id; }
public static int UseMarks(Employee employee, int i) { return employee.Marks; }
public static string UseFirstName(Employee employee, string s) { return employee.FirstName; }
public static string UseLastName(Employee employee, string s) { return employee.LastName; }
static public IEnumerable<Employee> Employees(IEnumerable<Employee> employees, func_returnInt where, func_returnInt orderby)
{
return employees.Where(i => where(i, 0) > 2).OrderBy(i => orderby(i, 0));
}
static public IEnumerable<Employee> Employees(IEnumerable<Employee> employees, func_returnInt where, func_returnString orderby)
{
return employees.Where(i => where(i, 0) > 2).OrderBy(i => orderby(i, ""));
}
}
public delegate int func_returnInt(Employee employee, int i);
public delegate string func_returnString(Employee employee, string s);

You have to use Expression : LambdaExpression.
http://msdn.microsoft.com/en-us/library/bb335710.aspx.
In my code i have used something similar:
private User GetUser(Expression<Func<User, bool>> query)
{
User user = context.Users.Where(query).FirstOrDefault();
if (user == null)
{
throw new ProviderException("The supplied user name could not be found.");
}
return user;
}
User user = GetUser(u => u.Marks == 123);
User user = GetUser(u => u.FirstName== "abc");

Related

How to use linq group with count and return ToListAsync to front end

This is my code. I need to use linq group and count after return to front end by ToListAsync.
But I got a error like this picture
How can I fix it?
public async Task<List<Product>> GetDataChart()
{
var data = await dbMLIMPORT.Product
.GroupBy(g => new { g.CMID, g.SUB_CODE })
.Select(s => new
{
CMID = s.Key.CMID,
SUB_CODE = s.Key.SUB_CODE,
COUNT = s.Sum(x => x.PRODUCT_ID.Length > 0 ? 1 : 0)
}).ToListAsync();
return data;
}
You need to create a new class ProductInfo and return a list of ProductInfos instead of a list of Products. Currently, you are creating anonymous type inside the select query and returning the same which will produce this error.
I hope, it will solve your problem.
public class ProductInfo
{
public string CMID {get; set;}
public string SUB_CODE {get; set;}
public int COUNT {get; set;}
}
public async Task<List<ProductInfo>> GetDataChart()
{
var data = await dbMLIMPORT.Product
.GroupBy(g => new { g.CMID, g.SUB_CODE })
.Select(s => new ProductInfo
{
CMID = s.Key.CMID,
SUB_CODE = s.Key.SUB_CODE,
COUNT = s.Sum(x => x.PRODUCT_ID.Length > 0 ? 1 : 0)
}).ToListAsync();
return data;
}

A circular reference was detected while serializing entities with one to many relationship

How to solve one to many relational issue in asp.net?
I have Topic which contain many playlists.
My code:
public class Topic
{
public int Id { get; set; }
public String Name { get; set; }
public String Image { get; set; }
---> public virtual List<Playlist> Playlist { get; set; }
}
and
public class Playlist
{
public int Id { get; set; }
public String Title { get; set; }
public int TopicId { get; set; }
---> public virtual Topic Topic { get; set; }
}
My controller function
[Route("data/binding/search")]
public JsonResult Search()
{
var search = Request["term"];
var result= from m in _context.Topics where m.Name.Contains(search) select m;
return Json(result, JsonRequestBehavior.AllowGet);
}
When I debug my code I will see an infinite data because Topics will call playlist then playlist will call Topics , again the last called Topic will recall playlist and etc ... !
In general when I just use this relation to print my data in view I got no error and ASP.NET MVC 5 handle the problem .
The problem happens when I tried to print the data as Json I got
Is there any way to prevent an infinite data loop in JSON? I only need the first time of data without call of reference again and again
You are getting the error because your entity classes has circular property references.
To resolve the issue, you should do a projection in your LINQ query to get only the data needed (Topic entity data).
Here is how you project it to an anonymous object with Id, Name and Image properties.
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new
{
Id = x.Id,
Name = x.Name,
Image = x.Image
});
return Json(result, JsonRequestBehavior.AllowGet);
}
If you have a view model to represent the Topic entity data, you can use that in the projection part instead of the anonymous object
public class TopicVm
{
public int Id { set;get;}
public string Name { set;get;}
public string Image { set;get;}
}
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new TopicVm
{
Id = x.Id,
Name = x.Name,
Image = x.Image
});
return Json(result, JsonRequestBehavior.AllowGet);
}
If you want to include the Playlist property data as well, you can do that in your projection part.
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new
{
Id = x.Id,
Name = x.Name,
Image = x.Image,
Playlist = x.Playlist
.Select(p=>new
{
Id = p.Id,
Title = p.Title
})
});
return Json(result, JsonRequestBehavior.AllowGet);
}

How to Assign Value to Array in List Object in C#

I have a List<Semesters> Object and Semesters class has Semester as String[] type. Below is my code which has error where Semester[1]= item.Semester. What is the correct syntax?
public class CourseList
{
public int? Prog_id { get; set; }
public List<Semesters> Series{ get; set; }
}
public class Semesters
{
public string[] Semester { get; set; }
public string Subject { get; set; }
public int Credit {get; set;}
}
[HttpGet]
[AllowAnonymous]
public CourseList GetCourseList(int Progid)
{
using (var context = new DataContext())
{
CourseList Obj = new CourseList();
List<Semesters> CObj = new List<Semesters>();
var qry = (from a in context.Courses
where a.Prog_id == Progid
orderby a.Semester
select a).ToList();
foreach (var item in qry)
{
Obj.Prog_id = item.Prog_id;
if (item.Semester.ToString() == "1st Semester")
{
CObj.Add(new Semesters {Semester[1]=item.Semester, Subject = item.Subject.ToString(), Coursetitle= item.Coursetitle });
}
if (item.Semester.ToString() == "2nd Semester")
{
CObj.Add(new Semesters { Semester[2] = item.Semester, Subject = item.Subject.ToString(), Coursetitle = item.Coursetitle });
}
Obj.Series = CObj;
}
return Obj;
}
You say Semester is an array of strings, so in this statement
Semester[1] = item.Semester
Semester is an array and item.Semester is an array.
Semester[1] should be assigned a string, but you are assigning it an array. You'll need to change it to (possibly...)
// assign a value from the item.Semester array.
Semester[1] = item.Semester[n]
That's one possibility w/o more info.

DocumentDb CreateDocumentQuery<T> returns items not of type T

I'm trying to get a list of documents from documentdb of specific object type -
_client.CreateDocumentQuery<RuleSetGroup>(_collectionLink)
.Where(f => f.SourceSystemId == sourceSystemId).AsEnumerable().ToList();
This returns objects of types other than RuleSetGroup, as long as they have a property SourceSystemId matching what I pass in. I understand this is how documentdb works, is there a way to enforce the type T so only those objects are returned?
I am using Auto Type Handling:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto
};
You will get different document types unless you implement a Type Pattern (adding a Type attribute to each Class) and use it as extra filter.
The reason is because you are storing NoSQL documents, which can obviously have different schema. DocumentDB treats them all equally, they are all documents; when you query, it's your responsability (because only you know the difference) to separate the different document types.
If you document Types all have an attribute "Client" (for example, Orders and Invoices) and you create a query with that attribute but mapped to one Type (Orders), you will get both Orders and Invoices that match the filter because they are documents that match the query. The deserialization logic is on your end, not within DocDB.
Here is an article regarding that Type Pattern when storing different document Types on DocDB (check the Base Type Pattern section).
Something like this might solve it:
public abstract class Entity
{
public Entity(string type)
{
this.Type = type;
}
/// <summary>
/// Object unique identifier
/// </summary>
[Key]
[JsonProperty("id")]
public string Id { get; set; }
/// <summary>
/// Object type
/// </summary>
public string Type { get; private set; }
}
public class RuleSetGroup : Entity
{
public RuleSetGroup():base("rulesetgroup")
}
public class OtherType : Entity
{
public OtherType():base("othertype")
}
_client.CreateDocumentQuery<RuleSetGroup>(_collectionLink).Where(f => f.Type == "rulesetgroup" && f.SourceSystemId == sourceSystemId).AsEnumerable().ToList();
You can wrap queries on helpers that set the type as a Where clause before applying your other filters (in LINQ you can chain Wheres without problem).
My repository might be a little too much for you, the short answer is that you can return .AsDocumentQuery() instead of .ToList()
public async Task<IEnumerable<T>> GetDocumentsAsync<T>(Expression<Func<T, bool>> predicate, int maxReturnedDocuments = -1,
bool enableCrossPartitionQuery = true, int maxDegreeOfParallellism = -1, int maxBufferedItemCount = -1)
{
//MaxDegreeofParallelism default = 0, add -1 to let SDK handle it instead of a fixed 1 network connection
var feedOptions = new FeedOptions
{
MaxItemCount = maxReturnedDocuments,
EnableCrossPartitionQuery = enableCrossPartitionQuery,
MaxDegreeOfParallelism = maxDegreeOfParallellism,
MaxBufferedItemCount = maxBufferedItemCount
};
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(_databaseName, _collectionName), feedOptions)
.Where(predicate)
.AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
var res = await query.ExecuteNextAsync<T>();
results.AddRange(res);
}
return results;
}
You can call the above method like this:
var ecsterConfigs = await repoBO.GetDocumentsAsync<EcsterPaymentConfig>(c => c.ValidTo == null && c.Type == type);
And then I have a wrapper around it sometimes when I "might" do an update of document, to keep track of the _Etag which will change if there is another update on the document before I write it down again.
public class DocumentWrapper<DocumentType>
{
public DocumentWrapper(Document document)
{
Value = (DocumentType)(dynamic)document;
ETag = document.ETag;
TimeStamp = document.Timestamp;
}
public DocumentType Value { get; set; }
public string ETag { get; set; }
public DateTime TimeStamp { get; set; }
}
#Granlund how do you make GetDocumentsAsync return DocumentWrapper instances while still allowing the predicate to query on the properties of the Value?
Here is what I came up with but maybe you have a better way:
[TestMethod]
[TestCategory("CosmosDB.IntegrationTest")]
public async Task AddAndReadDocumentWrapperViaQueryAsync()
{
var document = new Foo { Count = 1, Name = "David" };
var response = await client.CreateDocumentAsync(documentCollectionUri, document);
var id = response.Resource.Id;
var queryResult = await GetWrappedDocumentsAsync<Foo>(f => f.Where(a => a.Name == "David"));
foreach (var doc in queryResult)
{
Assert.AreEqual("David", doc.Value.Name);
}
}
public class Foo
{
public int Count { get; set; }
public string Name { get; set; }
}
public class DocumentWrapper<DocumentType>
{
public DocumentWrapper(Document document)
{
Value = (DocumentType)(dynamic)document;
ETag = document.ETag;
TimeStamp = document.Timestamp;
}
public DocumentType Value { get; set; }
public string ETag { get; set; }
public DateTime TimeStamp { get; set; }
}
public async Task<IEnumerable<DocumentWrapper<T>>> GetWrappedDocumentsAsync<T>(
Func<IQueryable<T>, IQueryable<T>> query,
int maxReturnedDocuments = -1,
bool enableCrossPartitionQuery = true,
int maxDegreeOfParallellism = -1,
int maxBufferedItemCount = -1)
{
//MaxDegreeofParallelism default = 0, add -1 to let SDK handle it instead of a fixed 1 network connection
var feedOptions = new FeedOptions
{
MaxItemCount = maxReturnedDocuments,
EnableCrossPartitionQuery = enableCrossPartitionQuery,
MaxDegreeOfParallelism = maxDegreeOfParallellism,
MaxBufferedItemCount = maxBufferedItemCount
};
IDocumentQuery<T> documentQuery =
query(client.CreateDocumentQuery<T>(documentCollectionUri, feedOptions)).AsDocumentQuery();
var results = new List<DocumentWrapper<T>>();
while (documentQuery.HasMoreResults)
{
var res = await documentQuery.ExecuteNextAsync<Document>();
results.AddRange(res.Select(d => new DocumentWrapper<T>(d)));
}
return results;
}

How can I retrieve/store a result set to an ArrayList?

How do I use the JdbcTemplate.query()/queryForList() to run a query using namedParameter and store the result set into a List of 'User's?
User Class:
public class User {
String name = null;
String id = null;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return name;
}
public void setId(String id) {
this.id = id;
}
}
Query:
SELECT name, id FROM USERS where email=:email
I'm looking for something like:
ArrayList<User> userList = jdbcTemplate.query(sql_query,
...some_mapper..., etc);
Seems like the answer to the question is not available at one place, on the Internet. Here's what I found out:
For adding the resultset into a List<>, we can use the NamedParameterJdbcTemplate.query() function:
NamedParameterJdbcTemplate jdbcTemplate;
ArrayList<User> usersSearchResult = (ArrayList<User>) jdbcTemplate.query(
USER_LIST_TP_query,
namedParameters,
new RowMapperResultSetExtractor<User>(new UserRowMapper(), 20));
We also have to define a custom RowMapperResultSetExtractor so that JDBC can understand how to convert each row in the result set to the type User.
private class UserRowMapper implements RowMapper<User> {
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getString("ID"));
user.setName(rs.getString("NAME"));
return user;
}
}

Resources