i'm trying to do some search inside some attributes of my object set but i'm getting some trouble on the right way to mount my linq query, i have my VT_Video class which has its attributes and some linked objects
public partial class VT_Video
{
public int ID { get; set; }
public string title { get; set; }
public string description { get; set; }
public virtual ICollection<VT_VideoTag> VT_VideoTag { get; set; }
}
public partial class VT_VideoTag
{
public int ID { get; set; }
public int tagID { get; set; }
public int videoID { get; set; }
public virtual VT_Tag VT_Tag { get; set; }
public virtual VT_Video VT_Video { get; set; }
}
public partial class VT_Tag
{
public int ID { get; set; }
public string name { get; set; }
public virtual ICollection<VT_VideoTag> VT_VideoTag { get; set; }
}
What i want to accomplish is search a user given word inside my Video collection by VT_Video.title, VT_Video.description and also by VT_Video.VT_VideoTag.VT_Tag.name, what i managed to do so far is only search the title and description:
var myVideos = db.VT_Video.Include("VT_VideoTag")
.Include("VT_VideoTag.VT_Tag")
.Where(vid =>
vid.descricao.Contains(strBusca) ||
vid.titulo.Contains(strBusca)).ToList();
Now, i know i can do what i want with some foreach and extra code but i wondered if it would be possible to do it using linq and also keep my code clean.
Thanks.
I have not worked with LINQ to SQL much, but it seems like .Any() would satisfy your requirement:
var myVideos = db.VT_Video.Include("VT_VideoTag")
.Include("VT_VideoTag.VT_Tag")
.Where(vid =>
vid.descricao.Contains(strBusca) ||
vid.titulo.Contains(strBusca) ||
vid.VT_VideoTag.Any(tag => tag.name.Contains(strBusca))).ToList();
Notice I added this clause:
vid.VT_VideoTag.Any(tag => tag.name.Contains(strBusca))
Which returns true if any tag in the collection has a name that contains your search string.
Related
I have a db with some tables in which items contains some localized string. The plan is to have an ID to a dedicated "Localization" table.
public class User
{
public int Id {get; set; }
public string Name { get; set; }
public Localization Signature { get; set; }
}
public class Item
{
public int Id {get; set; }
...
public Localization Title { get; set; }
}
public class Localization
{
public int Id { get; set; }
public string En { get; set; }
public string Fr { get; set; }
public string De { get; set; }
}
This works. But the goal is now to have a "dynamic" list of languages, so we could easy extend the localization to other languages.
I can solve this with a JSON field in which I serialize the languages string, but it has the disadvantage to lose the readability in any DB Viewer. So if possible, I would like to have real columns.
Is there any way to solve this use case with EF Core ?
I'd approach this not by adding columns to the database (which requires a change to the DB structure) but by adding rows:
public class Item
{
public int Id {get; set; }
...
public string Title { get; set; }
}
public class Localization
{
public int Id { get; set; }
public int ItemId { get; set; }
public string LanguageCode { get; set; }
public string Text { get; set; }
}
Then load item's title along the lines of:
item.Title = context.Localizations
.Where(l => l.ItemId == item.Id && l.LanguageCode == "en")
.FirstOrDefault();
(You can also load Title in the same query where you load Item... that query is meant to be illustrative of the concept).
{
"medic":[
{
"ace":[
{
"name":"lisinopril",
"strength":"10 mg Tab",
"dose":"1 tab",
"route":"PO",
"sig":"daily",
"pillCount":"#90",
"refills":"Refill 3"
}
],
"anti":[
{
"name":"nitroglycerin",
"strength":"0.4 mg Sublingual Tab",
"dose":"1 tab",
"route":"SL",
"sig":"q15min PRN",
"pillCount":"#30",
"refills":"Refill 1"
}
],
"anticoag":[
{
"name":"warfarin sodium",
"strength":"3 mg Tab",
"dose":"1 tab",
"route":"PO",
"sig":"daily",
"pillCount":"#90",
"refills":"Refill 3"
}
],
}
]
}
class Program
{
static void Main(string[] args)
{
// ""reporttype"":""post"",
string jsonString = #"..."; //The above json
Console.WriteLine("Enter the Medication name in which you want to Find STRENGTH value :");
string medicname = Console.ReadLine();
var rootInstance = JsonConvert.DeserializeObject<Rootobject>(jsonString);
}
}
var result = rootInstance.medications[0].Where(x=>x.name == medicname ).Select(t => t.strength).ToList();
But when i run the above query, I get this below error:
'Medication' does not contain a definition for 'Where' and no accessible extension method 'Where' accepting a first argument of type 'Medication' could be found (are you missing a using directive or an assembly reference?)
I have added all necessary namespaces to my code.
and Here is my object class
public class Rootobject
{
public List<Medication> medications { get; set; }
}
public class Medication
{
public List<aceInhibitors> aceinhibitors { get ; set ; }
public List<anti> antianginal {get; set; }
public List<anticoag> anticoagulants {get; set; }
}
public class aceInhibitors
{
[JsonProperty("name")]
public string name { get; set; }
[JsonProperty("strength")]
public string strength { get; set; }
[JsonProperty("dose")]
public string dose { get; set; }
[JsonProperty("route")]
public string route { get; set; }
[JsonProperty("sig")]
public string sig { get; set; }
[JsonProperty("pillCount")]
public string pillCount { get; set; }
[JsonProperty("refills")]
public string refills { get; set; }
}
public class anti
{
public string name { get; set; }
public string strength { get; set; }
public string dose { get; set; }
public string route { get; set; }
public string sig { get; set; }
public string pillCount { get; set; }
public string refills { get; set; }
}
public class anticoag
{
public string name { get; set; }
public string strength { get; set; }
public string dose { get; set; }
public string route { get; set; }
public string sig { get; set; }
public string pillCount { get; set; }
public string refills { get; set; }
}
Your Medication object itself is not searchable. Instead it holds a bunch of list and each contains a different type (where all properties are the same). So maybe you should use some base class for the medicine and add another property to your Medication class. In that case you would have a class layout something like this:
public class Rootobject
{
public List<Medication> medications { get; set; }
}
public class Medication
{
public List<aceInhibitors> aceinhibitors { get; set; }
public List<antianginal> antianginal { get; set; }
public List<anticoagulants> anticoagulants { get; set; }
public List<betaBlocker> betablocker { get; set; }
public List<diuretic> diuretic { get; set; }
public List<Mineral> mineral { get; set; }
public IEnumerable<Medicine> Medicines => Enumerable.Empty<Medicine>()
.Concat(aceinhibitors)
.Concat(antianginal)
.Concat(anticoagulants)
.Concat(betablocker)
.Concat(diuretic)
.Concat(mineral);
}
public class Medicine
{
[JsonProperty("name")]
public string name { get; set; }
[JsonProperty("strength")]
public string strength { get; set; }
[JsonProperty("dose")]
public string dose { get; set; }
[JsonProperty("route")]
public string route { get; set; }
[JsonProperty("sig")]
public string sig { get; set; }
[JsonProperty("pillCount")]
public string pillCount { get; set; }
[JsonProperty("refills")]
public string refills { get; set; }
}
public class aceInhibitors : Medicine
{
}
public class antianginal : Medicine
{
}
public class anticoagulants : Medicine
{
}
public class betaBlocker : Medicine
{
}
public class diuretic : Medicine
{
}
public class Mineral : Medicine
{
}
And prepared with that you could now ask something like that:
var result = rootInstance.medications[0].Medicines
.Where(x => x.name == medicname)
.Select(t => t.strength)
.ToList();
If the model of the classes really matches your desires is up to you, but it should give you starting point.
If you want it more inline you could also do something like this:
public class Medication : IEnumerable<Medicine>
{
public List<aceInhibitors> aceinhibitors { get; set; }
public List<antianginal> antianginal { get; set; }
public List<anticoagulants> anticoagulants { get; set; }
public List<betaBlocker> betablocker { get; set; }
public List<diuretic> diuretic { get; set; }
public List<Mineral> mineral { get; set; }
public IEnumerator<Medicine> GetEnumerator()
{
return Enumerable.Empty<Medicine>()
.Concat(aceinhibitors)
.Concat(antianginal)
.Concat(anticoagulants)
.Concat(betablocker)
.Concat(diuretic)
.Concat(mineral)
.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
And in that case you could write something like this:
var result = rootInstance.medications[0]
.Where(x => x.name == medicname)
.Select(t => t.strength)
.ToList();
Your domain model is bit suboptimal as it was pointed out by Oliver. If you need to stick to this model, then you can do the following.
Introduce an interface for fields that are interesting from your query point of view:
public interface InterestingFields
{
string name { get; }
string strength { get; }
}
Each medication class can be easily adjusted to implement it, like:
public class Mineral: InterestingFields
{
public string name { get; set; }
public string strength { get; set; }
public string dose { get; set; }
public string route { get; set; }
public string sig { get; set; }
public string pillCount { get; set; }
public string refills { get; set; }
}
Make the properties of the Medication class queryable
var properties = typeof(Medication).GetProperties()
.Where(prop => prop.PropertyType.IsGenericType
&& prop.PropertyType.GetGenericTypeDefinition() == typeof(List<>)
&& typeof(InterestingFields).IsAssignableFrom(prop.PropertyType.GetGenericArguments()[0]))
.ToList();
I've used reflection where the property's type is a List<T> and T is assignable to InterestingFields
Go through the properties, retrieve the actual value and do the filtering based on that
var medication = rootInstance.medications[0];
var result = from property in properties
let collection = property.GetValue(medication) as IEnumerable<InterestingFields>
let element = collection?.ToArray().First()
where element?.name == medicname
select element.strength;
Console.WriteLine(result.First());
Proper design would lead to a separation from the data handling and the way that your data is stored. This way, it is easy to reuse the stored data for other handling, it is easier to unit test the data handling with test code, you can change the way that the data is stored, to for instance a CSV file, or XML, without having to change the data handling code.
So you need a class Medication:
class Medication
{
public string Name {get; set;}
public string Strength {get; set;}
public string Dose {get; set;}
... // etc.
}
Consider to change Dose and Strength to a numerical value.
Apparently you have stored all Medications somewhere. A proper software design would hide where it is stored, and what format it is stored in. All you know is, that you can store Medications in it, and fetch it back later, even after your program is restarted. Such a storage is often called a Repository:
class MedicationRepository
{
public IEnumerable<Medication> ReadMedications() {...}
}
The actual implementation is up to you. I think you'll use Nuget Package NewtonSoft Json for this. Maybe you also want methods to Add / Change / Remove Medications?
Consider to let the Repository class implement IEnumerable<Medication>, or even ICollection<Medication>, depending on what is most efficient in your case.
class MedicationRepository : IEnumerable<Medication>
{
public IEnumerator<Medication> GetEnumerator()
{
return this.ReadMedications().GetEnumerator();
}
...
}
Now that you've got a method to read all Medications, we can get back to your LINQ problem:
I need get input string from user(which is medication name in json) i need to check if input matches the name in medication and need to display corresponding strength value.
So you've got a procedure to read the medication name:
public string ReadMedicationName() {...}
And you want the Strength of all Medications with this name.
MedicationRepository medications = ...
string requestedMedicationName = this.ReadMedicationName();
string medicationStrength = medications
.Where(medication => medication.Name == requestedMedicationName)
.Select(medication => medication.Strength)
.FirstOrDefault();
In words: from all Medications, keep only those Medications that have a name that equals requestedMedicationName. If the name is unique, then there will be zero or one Medication left. From all remaining Medications, take only the value of property Strength, and take the first strength, or null if there is no Medication with this Name at all.
Can it be that there are several Medications with this name? Which one do you want in that case, just any Strength (= .FirstOrDefault()), all Strengths (= ToList())? In the latter case: how do you distinguish which Medication with this name contains which Strength? Consider to Select more properties in that case.
Conclusion
By separating the storage of the data and how you get the requested Medication Name from the data handling, it is easier to change the storage (to XML, to CSV, to a database), and it is easier to unit test the LINQ using specific test data.
Similarly: you've hidden how you get the name of the requested Medication: is it a DOS prompt? Did you read it from a file? Maybe you've changed it to a WinForms application and you read it from a Textbox, or a ComboBox. Because you separated, the LINQ doesn't have to change, and can be reused in several platforms.
I have the following table
public class QTable
{
public string QID { get; set; }
public string QNO { get; set; }
public string Q1 { get; set; }
public string Q2 { get; set; }
public string opt1 { get; set; }
public string opt2 { get; set; }
public string opt3 { get; set; }
public string opt4 { get; set; }
public string Answer { get; set; }
public string Remarks { get; set; }
public string KnowledgeArea { get; set; }
public string Hints { get; set; }
public string Section { get; set; }
public string ToughLevel { get; set; }
public string DateCreated { get; set; }
public string Category { get; set; }
public string DeleteMe { get; set; }
}
QID is my primary key.
I have the
List<Qtable> qtableList = <list of all values of qtable from a query>
I need to find a particular QID and take other fields for manipulation.
Is there a SQL statement like structure to manipulate these things for a list?
You can use to get the item you want
Qtable item = qtableList.FirstOrDefault(x=>x.QID == id);
Just be aware that if the QID dont exists on the list and you try to access one of its properties, an 'Object reference is not an instance of an object exception' will be thrown, since you are trying to access a null object.
To prevent that, check if the return is differente of null prior to access and modify the properties you want.
In C# the best sql ike approach is just simply use linq, it's maded to handle this kind of request.
The basic syntax is:
var query = from qt in qtableList
where qt.QID == "1"
select qt;
This is basically returning the first pk element of your data.
Insert, update and other basic sql operations working as well.
More about on this:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/basic-linq-query-operations
I have a List<> which contains collection of objects after getting this list of BillSheetDetail I want to find that billWorkDetails[].details_classification =="xyz" and if it is found then fetch all the data of that particular array index of billWorksDetails[] and store it in other array to display.
How can I do this? I am new to C#
public class BillSheetDetail
{
public DateTime creation_date { get; set; }
public string customer_name { get; set; }
public string subject { get; set; }
public decimal tax_rate { get; set; }
public int total_amount { get; set; }
public string special_instruction { get; set; }
public string comment { get; set; }
public List<BillWorkDetail> billWorkDetails { get; set; }
}
[Serializable]
public class BillWorkDetail
{
public string product_name { get; set; }
public decimal quantity { get; set; }
public string unit { get; set; }
public int unit_cost { get; set; }
public int amount { get; set; }
public string remarks { get; set; }
public int row_no { get; set; }
public string details_classifiction { get; set; }
}
You have to combine Enumerable.Where and Any.
List<BillWorkDetail>[] matchingSheetDetails = billSheetDetailList
.Where(sd => sd.billWorkDetails.Any(d => d.details_classifiction == "xyz"))
.Select(sd => sd.billWorkDetails)
.ToArray();
This creates an array of all matching lists. Since your question is unclear, if you actually only want an array of the matching BillWorkDetail objects:
BillWorkDetail[] matchingBillWorkDetails = billSheetDetailList
.SelectMany(sd => sd.billWorkDetails.Where(d => d.details_classifiction == "xyz"))
.ToArray();
SelectMany selects all matching BillWorkDetail out of the List<BillSheetDetail>. Note that both approaches lose the reference to the BillSheetDetail instance from where it came from.
The solution is using the Where clause:
mySheetDetail.billWorkDetails.Where(x => x.details_classification == "xyz").ToList();
Here is a demonstration of the code that is working well: http://ideone.com/s2cUaR
Try this linq method
List<BillWorkDetail> myBillWorkDetails = new Lis<BillWorkDetail>();
myBillWorkDetails = myBillSheetDetail.billWorkDetails.Where(b => b.classifiction == "xyz").ToList();
This code retrieve all BillWorkDetail with classification xyz.
I want to return the item that has the profile ID I send. So in order to do this I will need to loop through all of the Items -> WebProproperties -> profile. The Class structure is at the end of the question.
I would rather use LINQ than create a nested foreach. I have been trying to get this to work for more than an hour now. I am stuck.
My first idea was to simply use where. But that doesn't work because you need to have something on the other side that needs to equal.
this.Accounts.items.Where(a => a.webProperties.Where(b => b.profiles.Where(c => c.id == pSearchString)) ).FirstOrDefault();
My second idea was to try using Exists which I don't have much experience with:
Item test = from item in this.Accounts.items.Exists(a => a.webProperties.Exists(b => b.profiles.Exists(c => c.id == pSearchString))) select item;
This doesn't work either:
Could not find an implementation of query pattern for source type 'Bool'
public RootObject Accounts {get; set;}
public class RootObject
{
public string kind { get; set; }
public string username { get; set; }
public int totalResults { get; set; }
public int startIndex { get; set; }
public int itemsPerPage { get; set; }
public List<Item> items { get; set; }
}
public class Profile
{
public string kind { get; set; }
public string id { get; set; }
public string name { get; set; }
public string type { get; set; }
}
public class WebProperty
{
public string kind { get; set; }
public string id { get; set; }
public string name { get; set; }
public string internalWebPropertyId { get; set; }
public string level { get; set; }
public string websiteUrl { get; set; }
public List<Profile> profiles { get; set; }
}
public class Item
{
public string id { get; set; }
public string kind { get; set; }
public string name { get; set; }
public List<WebProperty> webProperties { get; set; }
}
You can use Any() to determine existence. Also, note that many of the extension methods have overloads which take a predicate, including FirstOrDefault():
this.Accounts.items.FirstOrDefault(a => a.webProperties
.Any(b => b.profiles
.Any(c => c.id == pSearchString)));
You are looking for the .Any() operation I think. This will return true/false for whether there are any items matching your query.
For example:
if (this.Accounts.Items.Any(i=>i.webProperties.Any(wp=>wp.profiles.Any(p=>p.id == MySearchId)));
EDIT: You have full answer (was posted while I was composing mine) and as pointed out in comments my answer isn't actually returning your found item, just letting you know whether there is one. You can rework the first .Any to be a .FirstOrDefault to get that match.
E.g.
var result = this.Accounts.Items.FirstOrDefault(i=>i.webProperties.Any(wp=>wp.profiles.Any(p=>p.id == MySearchId)))
You can use the below mentioned code.
var abc = rr.items.Where(p => p.webProperties.Any(c => c.profiles.Any(d => d.id == "1"))).FirstOrDefault();
Just for your reference, your class should look like:
public class RootObject
{
public string kind { get; set; }
public string username { get; set; }
public int totalResults { get; set; }
public int startIndex { get; set; }
public int itemsPerPage { get; set; }
private List<Item> _items=new List<Item>();
public List<Item> items
{
get { return _items; }
set { _items = value; }
}
}