Dynamic LINQ to Entities, how to build query based on variables - c#

The query I need to build is this:
query = query.Where(s =>
(
(s.Title.Contains(title1) && s.EpisodeTitle.Contains(episodeTitle1))
||
(s.Title.Contains(title2) && s.EpisodeTitle.Contains(episodeTitle2)))
);
The only issue is, s.Title and s.EpisodeTitle are dynamic.
Meaning that the following variables could be part of the query:
(string title1 = null,
string title2 = null,
string episodeTitle1 = null,
string episodeTitle2 = null,
string genre = null,
string directorName = null,
string releaseYear = null,
string seasonEpisode = null,
string showTypeDescription = null)
e.g.
query = query.Where(s =>
(
(s.DirectorName.Contains(directorName) && s.ShowTypeDescription.Contains(ShowTypeDescription))
||
(s.releaseYear.Contains(releaseYear) && s.genre.Contains(genre)))
);
In ANY type of combination.
How can I construct this query without having to take into account EVERY SINGLE possibility here?

If you only need AND logic you could just call .Where() repeatedly for every attribute that requires searching on.
if(title != null) query = query.Where(x=>x.Title == title);
if(genre != null) query = query.Where(x=>x.Genre == genre);
If your query is always of a certain structure and you want to ignore null search values you could do one big query but short circuit the attribute comparison with null checks.
query = query.Where(s =>
(
((title1 == null || s.Title.Contains(title1))
&& (episodeTitle1 == null || s.EpisodeTitle.Contains(episodeTitle1))
||
((title2 == null || s.Title.Contains(title2))
&& (episodeTitle2 == null || s.EpisodeTitle.Contains(episodeTitle2))))
);
However if you need full control over the query then you will need to look at using PredicateBuilder or System.Linq.Expressions to build a specific query to search on the necessary attributes. Here is a useful tutorial on Linq.Expressions - http://msdn.microsoft.com/en-us/library/vstudio/bb882637.aspx

the best solution is to use linqExtension with LINQKIT.
using (var context = new workEntities() )
{
Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
dictionary["Title"] = new List<string> {
"Network Engineer",
"Security Specialist",
"=Web Developer"
};
dictionary["Salary"] = new List<string> { ">=2000" };
dictionary["VacationHours"] = new List<string> { ">21" };
dictionary["SickLeaveHours"] = new List<string> { "<5" };
dictionary["HireDate"] = new List<string> {
">=01/01/2000",
"28/02/2014"
};
dictionary["ModifiedDate"] = new List<string> { DateTime.Now.ToString() };
var data = context.Employee.CollectionToQuery(dictionary).ToList();
}

Related

How to set dynamic column in linq query

I have following code,
public List<MemberDto> GetMembers(out int rowCount,int pageIndex,int pageSize, string seachBy = "", string searchTerm = "", string sortBy = "", string sortDiection = "")
{
var members = (from m in context.Members
where (string.IsNullOrEmpty(searchTerm) || m.MemberNumber.Equals(searchTerm))
|| (string.IsNullOrEmpty(searchTerm) || m.LastName.Equals(searchTerm))
select m).AsEnumerable();
if (!string.IsNullOrEmpty(sortBy))
{
PropertyDescriptor prop = TypeDescriptor.GetProperties(typeof(EFModel.ClientData.Member)).Find(sortBy, true);
members = (sortDiection.ToLower() == "descnding") ? members.OrderByDescending(x => prop.GetValue(x)).ToList() : members.OrderBy(x => prop.GetValue(x)).ToList();
}
rowCount = (!string.IsNullOrEmpty(searchTerm)) ? members.Count() : rowCount = context.Members.Count() ;
members = members.Skip(pageIndex).Take(pageSize).ToList();
List<MemberDto> memberDtos = new List<MemberDto>();
mapper.Map(members, memberDtos);
return memberDtos;
}
In the above method string seachColumn value can be memberno or lastname or sometime empty. when seachColumn value is memberno. I only need to search searchTerm value in MemberNumber column.
when seachColumn value is lastname. I only need to search searchTerm value in LastName column.
sometimes searchTerm can be empty. when it happen I need all the records without considering any search terms.
Above query is working bit. the problem is that when there is a value for searchTerm, That result is given regardless of the column. How can i do this.
public List<MemberDto> GetMembers(out int rowCount,int pageIndex,int pageSize, string seachColumn = "", string searchTerm = "", string sortBy = "", string sortDiection = "")
{
var query = context.Members;
if(string.IsNullOrWhitespace(searchTerm)
{
return query.ToList();
}
//if you want to check strings equality in ignore-case mode, you should change your db collation using the link below.
return query
.Where(m => m.MemberNumber.Equals(searchTerm) || m.LastName.Equals(searchTerm))
.ToList();
}
String equality check in ef using db collation

AsEnumerable Dynamic Fields in Where & Select

I want to use the distinct clause for multiple levels. Firstly i tried with DataTable but did't got success so i converted DataTable to AsEnumerable.
My problem here is that the Fields which i have specified/hard coded will be coming dynamic, same for both Where & Select.
How to add dynamic Fields in Where & Select?
DataTable data3 = new DataTable();
var listData = data3.AsEnumerable()
.Where(m => !String.IsNullOrEmpty(m.Field<string>("clientname"))
&& !String.IsNullOrEmpty(m.Field<string>("project"))
&& !String.IsNullOrEmpty(m.Field<string>("postedstate"))
&& !String.IsNullOrEmpty(m.Field<string>("postedcity"))
&& !String.IsNullOrEmpty(m.Field<string>("siteadd")))
.Select(row => new
{
clientname = row.Field<string>("clientname"),
project = row.Field<string>("project"),
postedstate = row.Field<string>("postedstate"),
postedcity = row.Field<string>("postedcity"),
siteadd = row.Field<string>("siteadd")
}).Distinct();
You could do something similar to this:
string clientName = "my client";
string project = null;
DataTable data3 = new DataTable();
var listData = data3.AsEnumerable().Where(m =>
(String.IsNullOrEmpty(clientName) || m.Field<string>("clientname") == clientName)
&& (String.IsNullOrEmpty(project) || m.Field<string>("project") == project)
).Select(row => new Project()
{
clientname = row.Field<string>("clientname"),
project = row.Field<string>("project"),
postedstate = row.Field<string>("postedstate"),
postedcity = row.Field<string>("postedcity"),
siteadd = row.Field<string>("siteadd")
}).Distinct();
This way you will have no need to have an anonymous type returned and get rid of the problem.

Linq to Sql - return all data if 'contains' value is null in where clause

I need to get all vendorname if value(data.vendorname) is null its give an error Exception: "Value cannot be null."
public HttpResponseMessage PostFilter([FromBody] dynamic data)
{
string[] vendorname = data.vendorname != null
? data.vendorname.ToObject<string[]>()
: null;
var items = (from s in context.AllInventories
where
(vendorname!=null
? vendorname.Contains(s.VENDORNAME)
:1==1)
select s)
.Take(500)
.ToList();
}
Why don't you simplify this, by just not applying any where at all if vendorname is null?
public HttpResponseMessage PostFilter([FromBody] dynamic data)
{
string[] vendorname = data.vendorname != null
? data.vendorname.ToObject<string[]>()
: null;
var query = context.AllInventories.AsQueryable();
if (vendorname != null)
{
query = query.Where(s => vendorname.Contains(s.VENDORNAME));
}
var items = query.Take(500).ToList();
}
Why don't you use a simple if-statement?
IEnumerable<Inventory> inventories = context.AllInventories;
if(vendorname != null)
inventories = inventories.Where(i => vendorname.Contains(i.VENDORNAME));
inventories = inventories.Take(500).ToList();
That's much better than hoping that your sql trick works and that the optimizer is clever enough to ignore your pseudo-condition. It's also better to debug.

How to use replace function in linq

I am trying to create a replace function, which can replace multiple strings within the 'body' fieldname of my linq queries.
This is my linq function:
public string GetReplacedText(string body)
{
var data = from c in db.StoryTbls
where c.Body == body
select new
{
c.Body
};
Regex rgx2 = new Regex("<P align=right><A href>RS</A></P>");
Regex rgx3 = new Regex("<A href>ML</A>");
string res = rgx2.Replace(body, "");
res = rgx3.Replace(body,"");
return res;
}
I am calling the above function, into the below 'httpresponse' method.
public HttpResponseMessage getData()
{
if (User.IsInRole("admin"))
{
Regex rgx2 = new Regex("<A href></A>");
var join = (from s in db.StoryTbls
join c in db.META_Cat on s.Theme equals c.metaIndex
where s.ACTIVE == true
&& s.PUB_ID == 250
&& c.Categories == "RM"
orderby s.ACTIVEDATE descending
select new
{
s.TITLE,
s.Body,
s.ACTIVEDATE,
c.Categories
}).AsEnumerable().Select(c => new NewObj
{
Title = c.TITLE,
Body = GetReplacedText(c.Body),
ActiveDate = c.ACTIVEDATE,
Categories = c.Categories
}).Take(50).ToList();
var data = join.ToList();
if (!data.Any())
{
var message = string.Format("No data found");
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
However, when I call the API, the body content data is not replaced. Hence the function not working.
Please advise, as to where I may be going wrong.
Many thanks
You need to take the output from your first call to Replace and pass it in to your second call to Replace, like so:
string res = rgx2.Replace(body, "");
res = rgx3.Replace(res, "");

What can be used instead of Datatable in LINQ

I have a SQL query that returns a Datatable:
var routesTable = _dbhelper.Select("SELECT [RouteId],[UserId],[SourceName],[CreationTime] FROM [Routes] WHERE UserId=#UserId AND RouteId=#RouteId", inputParams);
and then we can work with Datatable object of routesTable
if (routesTable.Rows.Count == 1)
{
result = new Route(routeId)
{
Name = (string)routesTable.Rows[0]["SourceName"],
Time = routesTable.Rows[0]["CreationTime"] is DBNull ? new DateTime() : Convert.ToDateTime(routesTable.Rows[0]["CreationTime"])
};
result.TrackPoints = GetTrackPointsForRoute(routeId);
}
I want to change this code to linq but I don't know how can I simulate Datatable in LINQ ,I wrote this part:
Route result = null;
aspnetdbDataContext aspdb = new aspnetdbDataContext();
var Result = from r in aspdb.RouteLinqs
where r.UserId == userId && r.RouteId==routeId
select r;
....
but I don't know how can I change this part:
if (routesTable.Rows.Count == 1)
{
result = new Route(routeId)
{
Name = (string)routesTable.Rows[0]["SourceName"],
Time = routesTable.Rows[0]["CreationTime"] is DBNull ? new DateTime() : Convert.ToDateTime(routesTable.Rows[0]["CreationTime"])
};
would you please tell me how can I do this?
EDIT
here you can see the whole block of code in original
public Route GetById(int routeId, Guid userId)
{
Route result = null;
var inputParams = new Dictionary<string, object>
{
{"UserId", userId},
{"RouteId", routeId}
};
var routesTable = _dbhelper.Select("SELECT [RouteId],[UserId],[SourceName],[CreationTime] FROM [Routes] WHERE UserId=#UserId AND RouteId=#RouteId", inputParams);
if (routesTable.Rows.Count == 1)
{
result = new Route(routeId)
{
Name = (string)routesTable.Rows[0]["SourceName"],
Time = routesTable.Rows[0]["CreationTime"] is DBNull ? new DateTime() : Convert.ToDateTime(routesTable.Rows[0]["CreationTime"])
};
result.TrackPoints = GetTrackPointsForRoute(routeId);
}
return result;
}
SELECT Function:
public DataTable Select(string query, Dictionary<string, object> parameters)
{
var dt = new DataTable();
using (_command = new SqlCommand(query, _connnection))
{
InitializeParametersAndConnection(parameters);
using (_adapter = new SqlDataAdapter(_command))
{
_adapter.Fill(dt);
}
}
return dt;
}
and the GetTrackPointsForRoute
private List<TrackPoint> GetTrackPointsForRoute(int routeId)
{
aspnetdbDataContext aspdb = new aspnetdbDataContext();
var result = new List<TrackPoint>();
var trackPointsTable = from t in aspdb.TrackPointlinqs
where t.RouteFK == routeId
select t;
foreach (var trackPointRow in trackPointsTable)
{
var trackPoint = new TrackPoint
{
Id = (int)trackPointRow.TrackPointId,
Elevation = Convert.ToSingle(trackPointRow.Elevation),
Latitude = Convert.ToDouble(trackPointRow.Latitude),
Longitude = Convert.ToDouble(trackPointRow.Longitude),
Time = trackPointRow.TrackTime is DBNull ? new DateTime() : (DateTime)trackPointRow.TrackTime
};
result.Add(trackPoint);
}
return result;
}
var firstRoute = aspdb.RouteLinqs
.Where(r => r.UserId == userId && r.RouteId == routeId)
.FirstOrDefault();
if (firstRoute == null)
{
return null;
}
else
{
return new Route(routeId)
{
Name = first.SourceName,
Time = first.CreationTime ?? new DateTime(),
TrackPoints = GetTrackPointsForRoute(routeId)
};
}
If this is LINQ to SQL you can simplify it further (this won't work with LINQ to Entity Framework though):
return aspdb.RouteLinqs
.Where(r => r.UserId == userId && r.RouteId == routeId)
.Select(r => new Route(routeId)
{
Name = r.SourceName,
Time = r.CreationTime ?? new DateTime(),
TrackPoints = GetTrackPointsForRoute(routeId)
})
.FirstOrDefault();
Note: You probably can replace GetTrackPointsForRoute with a join to the child table, meaning that the entire method can be done with a single call to the database, rather than one call to get the routes, and a second call to get the points. To do this you should learn about associations and joins in LINQ to SQL.

Categories