filtering list using linq - c#

I am writing to seek help, in how can I filter data and then merge two results, below:
Update code*
public class TestController : ApiController
{
private cdw db = new cdw();
public HttpResponseMessage get([FromUri] Query query)
{
var data = db.data_qy.AsQueryable();
if (query.startDate != null)
{
data = data.Where(c => c.UploadDate >= query.startDate);
}
if (!string.IsNullOrEmpty(query.tag))
{
var ids = query.tag.Split(',');
data = data.Where(c => c.TAG.Any(t => ids.Contains(t)));
}
if (!string.IsNullOrEmpty(query.name))
{
var ids = query.name.Split(',');
data = data.Where(c => c.NAME.Any(t => ids.Contains(t)));
}
if (!data.Any())
{
var message = string.Format("No data found");
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
return Request.CreateResponse(HttpStatusCode.OK, data);
}
}
entity class:
public partial class data_qy
{
public int ID { get; set; }
public string Name { get; set; }
public string TAG { get; set; }
public string TAG_IS { get; set; }
[Newtonsoft.Json.JsonProperty(PropertyName = "Date")]
public Nullable<System.DateTime> UploadDate { get; set; }
}
Sample Dataset:
Name Tag Tag_IS
AMCAR 2013-5 03065EAC9
ARES 2006-6RA 04009JAA9
ARES 2012-1A 04013TAB9
ATOM 2003-I A 0182690668
BACM 2006-2 AM 05950EAG3
BCAP 2007-AA3 05530VAN9
BCAP 2007-AA3 05530VAN9
BCJAF 9 C 0312888037
BLNDLN 0 0213093627
BLNDLN 0 0213093627
The underlying SQL query should resemble:
select *
from [dbo].[data_qy]
where TAG like '%78473TAC4%'
or TAG LIKE '%05946XYZ0%'
OR NAME LIKE '%TAP%'
OR NAME LIKE '%STORM%'
Using the following list method above, when I run a query such as (api/test/tag=78473,12669,05946,... (30 values)), i get a -- Exception Message:
Some part of your SQL statement is nested too deeply. Rewrite the query or break it up into smaller queries
Am I missing something. Any help would be most appreciated.
Thanks

I'm not in a situation to test this on the spot, but I suspect that either of the following should work:
public class TestController : ApiController
{
private cdw db = new cdw();
public HttpResponseMessage get([FromUri] Query query)
{
IQueryable<data_qy> data = null;
if (!string.IsNullOrEmpty(query.tag))
{
var ids = query.tag.Split(',');
var dataMatchingTags = db.data_qy.Where(c => ids.Any(id => c.TAGS.Contains(id)));
if (data == null)
data = dataMatchingTags;
else
data = data.Union(dataMatchingTags);
}
if (!string.IsNullOrEmpty(query.name))
{
var ids = query.name.Split(',');
var dataMatchingName = db.data_qy.Where(c => ids.Any(id => c.NAME.Contains(id)));
if (data == null)
data = dataMatchingName;
else
data = data.Union(dataMatchingName);
}
if (data == null) // If no tags or name is being queried, apply filters to the whole set of products
data = db.data_qy;
if (query.startDate != null)
{
data = data.Where(c => c.UploadDate >= query.startDate);
}
var materializedData = data.ToList();
if (!materializedData.Any())
{
var message = string.Format("No data found");
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
return Request.CreateResponse(HttpStatusCode.OK, materializedData);
}
}
I also suspect that you don't need to check against Null in your query, since EF will understand that when transforming the Expression Tree to SQL, but if needed, you can add it.
That would remove the need to use the foreach, the Aggregate and the call to Count. and results in a much simpler query that should use the IN operator in SQL.
Currently you're executing the same query multiple times (the call to .Any at the end will execute the query and then passing the data variable will execute it again. This can be very costly. Instead, materialize the results and act upon that, as above.

Related

Best way to append query string parameter to URL from object

I have query string class.
public class PagingModel
{
public int PageNumber { get; set; } = 1;
public string Filter { get; set; } = "text";
}
string url = "Menu/GetMenus";
I have to generate the URI with a query string based on an object in ASP.NET Core 5 preview. Is there any built in query helper?.
Required output:
/Menu/GetMenus?PageNumber=3&Filter=text
MVC Controller:
public async Task<IActionResult> index_partial([FromQuery] PagingModel paging)
{
var data = await _apiService.GetMenusAsync(paging);
return PartialView("_IndexPartial", data);
}
Service:
public async Task<PagedList<MenuModel>> GetMenusAsync(PagingModel paging)
{
string Requiredurl = "Menu/GetMenus?page="+ paging.PageNumber;
}
I got this extension method.. No need to generate query string manually.Only class object we need to pass. i thought some one else can use the same thing ...
public static string AppendObjectToQueryString(string uri, object requestObject)
{
var type = requestObject.GetType();
var data = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary
(
p => p.Name,
p => p.GetValue(requestObject)
);
foreach (var d in data)
{
if (d.Value == null)
{
continue;
}
if ((d.Value as string == null) && d.Value is IEnumerable enumerable)
{
foreach (var value in enumerable)
{
uri = QueryHelpers.AddQueryString(uri, d.Key, value.ToString());
}
}
else
{
uri = QueryHelpers.AddQueryString(uri, d.Key, d.Value.ToString());
}
}
return uri;
}
Ex: In my case i called this way.
string uri = "Menu/GetMenus";
string full_uri = QueryStringExtension.AppendObjectToQueryString(uri, paging);
With a Query String this simple I would just do
PagingModel qsData = new PagingModel();
//set qsData properties as needed
string urlWithQueryString = $"/Menu/GetMenus?{nameof(PagingModel.PageNumber)}={qsData.PageNumber}&nameof(PagingModel.Filter)}={qsData.Filter}";
However more standard is to do something like
string urlWithQueryString = this.Url.Action("GetMenus", "Menu", new PagingModel { PageNumber = 3, Filter = "text" }, this.Request.Url.Scheme);
But best solution depends on your specific case - can you add your action method definition for GetMenus ?
Update for your additional code :
Seeing as looks like you want to generate the url inside the service I would simply do this :
public async Task<PagedList<MenuModel>> GetMenusAsync(PagingModel paging)
{
string Requiredurl = $"/Menu/GetMenus?{nameof(PagingModel.PageNumber)}={paging.PageNumber}&nameof(PagingModel.Filter)}={paging.Filter}";
}

system.collections.generic.list to system.collections.generic.ienumerable

I have a method where list cannot convert to ienumerable. How do i cast???
public ActionResult Attending()
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier).Value;
var gigs = _context.Attendances.Include(a => a.Gig.Artist).Include(a => a.Gig.Genre).Where(a => a.AttendeeId == userId).ToList();
var viewModel = new GigsViewModel()
{
UpcomingGigs = gigs,
ShowActions = User.Identity.IsAuthenticated,
Heading = "Gigs I'm Attending"
};
return View("Gigs", viewModel);
}
Here is my ViewModel:
public class GigsViewModel
{
public IEnumerable<Gig> UpcomingGigs { get; set; }
public bool ShowActions { get; set; }
public string Heading { get; set; }
}
Try using below code, AsEnumerable() will do your work
var gigs = _context.Attendances.Where(a => a.AttendeeId == userId).select(a=>
new Gig()
{
property = a.property //assuming property, you can repeat the same for other properties of Gig
}).AsEnumerable();
The result of a Linq database query is typically IQueryable which is derived from IEnumerable, IQueryable, and IEnumerable.
In your case,it's linq database query so you need to call AsEnumerable();
You can also first fetch all attendees, and select the gigs.
IEnumerable<Gig> gigs = _context.Attendances.Where(a => a.AttendeeId == userId).Select(a => a.Gig).ToList();
Be aware if there can be multiple similar gigs, then perhaps you need only distinct set of gigs using Distinct:
IEnumerable<Gig> gigs = _context.Attendances.Where(a => a.AttendeeId == userId).Select(a => a.Gig).Distinct().ToList();
See more at: https://msdn.microsoft.com/en-us/library/bb534803(v=vs.110).aspx
Edited: edited the old suggestion, Distinct and Select added.

Dynamic results using dapper in mvc

I am trying the below way to return the dynamic results using dapper and stored procedure. Am I doing it in correct way?
using (IDbConnection dbConnection = Connection)
{
dbConnection.Open();
var result = dbConnection.Query<dynamic>("LMSSP_GetSelectedTableData",
new
{
TableName = TableName,
LangaugeID = AppTenant.SelectedLanguageID,
UserID = AppTenant.UserID
}, commandType: CommandType.StoredProcedure).ToList();
if (result != null)
{
// Added just for checking the data
foreach (var item in (IDictionary<string, object>)result.FirstOrDefault())
{
string key = item.Key;
string value = item.Value.ToString();
}
}
}
What my stored procedure do is, I will pass any table name and based on that it will return the results/records.So, obviously my number of records, columns will be varied as per the table name passed.
To achieve this I have used dynamic keyword along with dapper.
So my question is how can I pass this data to view as a model and render the controls on the view as per the properties/column data type. Can I get the data type of column OR PropertyInfo?
But, when dapper retrieves the records from database it returns as dapper row type?
Using same SP to fetch data from different table would be confusing (not good design). However to solve your problem technically, you can create model having list of control information. Example of control information
public class ControlInformation
{
public string Name { get; set; }
public dynamic Value { get; set; }
public string ControlType { get; set; }
// Applicable for drop down or multi select
public string AllValues { get; set; }
}
Model will have list of ControlInformations
public List<ControlInformation> ControlInformations { get; set; }
View will render the controls (partial view based on control type) Ex: very basic case to render different view for int and another view for rest. I have 2 partial views "IntCtrl" and "StringCtrl".
#foreach (var item in Model.ControlInformations)
{
if (#item.ControlType == "System.Int32")
{
Html.RenderPartial("IntCtrl", item);
}
else
{
Html.RenderPartial("StringCtrl", item);
}
}
Hope this help.
Here we are calling method which returns Datatable :
public DataTable GetMTDReport(bool isDepot, int userId)
{
using (IDbConnection _connection = DapperConnection)
{
var parameters = new DynamicParameters();
parameters.Add("#IsDepot", isDepot);
parameters.Add("#userId", userId);
var res = this.ExecuteSP<dynamic>(SPNames.SSP_MTDReport, parameters);
return ToDataTable(res);
}
}
In this we can call stored procedures by calling our custom method "ExecuteSP" :
public virtual IEnumerable<TEntity> ExecuteSP<TEntity>(string spName, object parameters = null)
{
using (IDbConnection _connection = DapperConnection)
{
_connection.Open();
return _connection.Query<TEntity>(spName, parameters, commandTimeout:0 , commandType: CommandType.StoredProcedure);
}
}
and here is "DapperConnection" method to connect the database:
You can give connection string with key ["MainConnection"]
public class DataConnection
{
public IDbConnection DapperConnection
{
get
{
return new SqlConnection(ConfigurationManager.ConnectionStrings["MainConnection"].ToString());
}
}
}
And at last we call "ToDataTable" method to change our response in datatable . We will receive response in DapperRow from the database because we passsed dynamic type in stored procedure.
public DataTable ToDataTable(IEnumerable<dynamic> items)
{
if (items == null) return null;
var data = items.ToArray();
if (data.Length == 0) return null;
var dt = new DataTable();
foreach (var pair in ((IDictionary<string, object>)data[0]))
{
dt.Columns.Add(pair.Key, (pair.Value ?? string.Empty).GetType());
}
foreach (var d in data)
{
dt.Rows.Add(((IDictionary<string, object>)d).Values.ToArray());
}
return dt;
}

Check which elements are on one list comparing to another list LINQ

I have two lists, one of all languages and another subset of languages that the site has, the idea is to return all the languages but change the property of a boolean if the element of the subset corresponds to the list of all languages.
DTO of language:
public class DTOLanguage
{
public bool HaveLanguage { get; set; }
public int IdLanguage { get; set; }
//Other properties...
}
Method that returns all languages:
public List<DTOLanguage> GetLanguages()
{
var result = repository.RepSite.GetLanguages().Select(x => new DTOLanguage
{
IdLanguage = x.IdLanguage,
CodName = x.CodName,
Name = x.Name
}).ToList();
return result;
}
Method that returns the subset of languages:
public List<DTOLanguage> GetLanguagesById(int idSite)
{
var result = repository.RepSite.GetLanguagesById(idSite).Select(x => new DTOLanguage
{
IdLanguage = x.IdLanguage
}).ToList();
return result;
}
The GetLanguagesById is called in the DataAccess layer, so what Im thinking is that this method should receive another parameter (what GetLanguages returns) and make some fancy LINQ there.
I know that I can filter (example):
SubsetOfLanguages.Where(lg => lg.IdLanguage == AllLanguagesItem.IdLanguage)
{
AllLanguagesItem.HaveLanguage = True;
}
But Im not really sure as how it should be.
Thanks in advance.
You could use Contains extension method this way:
var languages=GetLanguages();
var subsetids=repository.RepSite.GetLanguagesById(idSite).Select(x =>x.IdLanguage);//Select just the id value
foreach(var l in languages.Where(l=>subsetids.Contains(l.IdLanguage)))
{
l.HaveLanguage = true;
}
You could do this:
var allLanguages = GetLanguages();
var subset = SubsetOfLanguages
.Where(lg => allLanguages.Any(a => lg.IdLanguage == a.IdLanguage))
.ToArray();
foreach(var item in subset)
{
item.HaveLanguage = True;
}

MongoDB & Web API - can't understand how to use filters

I am trying to do basic CRUD on a MongoDB using C# Web API. All the examples online are for deprecated methods and I simply can't get the new methods to work.
the places where i need help in the code bellow:
Delete all items in collection - syntax to remove all.
get all items in collection - filter for get all
get by id = syntax to select by id
i've tried many examples online. most are deprecated for previous versions of MongoDB and the official Mongo docs are not helping, i suspect as my code is involving WebAPI classes and the examples are not for that.
thanks for your help!
This is the class:
public class template
{
[BsonId]
public string templateUniqueId { get; set; }
public string outsideClientId { get; set; }
public string ClientId { get; set; }
public string templateFieldsData { get; set; }
public bool isActive { get; set; }
}
And my repository implementation (part of if):
public class templateRepository : ItemplateRepository
{
public MongoClient client;
public IMongoDatabase database;
public IMongoCollection<template> templatesCollection;
public templateRepository()
{
string connectionString = ConfigurationManager.AppSettings["mongoconnection"];
if (string.IsNullOrWhiteSpace(connectionString))
{
connectionString = "mongodb://localhost:27017";
}
client = new MongoClient(connectionString);
database = client.GetDatabase(ConfigurationManager.AppSettings["mongo_personal"]);
templatesCollection = database.GetCollection<template>("templates");
//var persons = await collection.Find(new BsonDocument()).ToListAsync();
// Reset database and add some default entries
FilterDefinition<template> f;
var result = templatesCollection.DeleteMany(f); // this line should remove all items
for (int index = 1; index < 5; index++)
{
template template1 = new template
{
templateUniqueId = string.Format("t{0}", index.ToString()),
outsideClientId= string.Format("t{0}", index.ToString()),
ClientId = string.Format("t{0}", index.ToString()),
isActive = true,
templateFieldsData = "sharon"
};
AddTemplate(template1);
}
}
public void AddTemplate(template templateIn)
{
database.GetCollection<template>("templates").InsertOne(templateIn);
}
public IEnumerable<template> GetAllTemplates()
{
var templates = database.GetCollection<template>("templates").Find({ }); // get all templates
return templates;
}
public template GetTemplate(string id)
{
IMongoQuery query = Query.EQ("_id", id);
return templatesCollection.Find(query).FirstOrDefault();
}
public bool UpdateTemplate(string id, template item)
{
IMongoQuery query = Query.EQ("_id", id);
item.LastModified = DateTime.UtcNow;
IMongoUpdate update = Update
.Set("outsideClientId", item.outsideClientId)
.Set("ClientId", item.ClientId)
.Set("isActive", item.isActive)
.Set("templateFieldsData", item.templateFieldsData);
SafeModeResult result = templatesCollection.Update(query, update);
return result.UpdatedExisting;
}
}
You are using the legacy driver methods, here are some examples for MongoDB C# Driver 2.2:
Delete all items in collection, syntax to remove all.
coll.DeleteMany(FilterDefinition<template>.Empty);
Get all items in collection, filter for get all
var results = coll.Find(FilterDefinition<template>.Empty).ToList();
Get by id, syntax to select by id
var filter = Builders<template>.Filter.Eq(t=>t.templateUniqueId, id);
var result = coll.Find(filter).FirstOrDefault();
Also, for your update method, you can use the following syntax:
var filter = Builders<template>.Filter.Eq(t=>t.templateUniqueId, id);
var update = Builders<template>.Update
.Set(t=>t.outsideClientId, item.outsideClientId)
.Set(t=>t.ClientId, item.ClientId)
.Set(t=>t.isActive, item.isActive)
.Set(t=>t.templateFieldsData, item.templateFieldsData);
var updateResult = coll.UpdateOne(filter, update);
return result.ModifiedCount > 0;
More information about Definitions and Builders:
http://mongodb.github.io/mongo-csharp-driver/2.0/reference/driver/definitions/

Categories