how to query many to many relationship in Linq to EF5 - asp.net

I am learning EF5 and building a small website which simply displays some songs and singers. As a song can be sung by more than one singer and a singer will have many songs so my EF model as below.
I want to display all the list of songs with its relevant singers in a table so I wrote a query and this is so far I have.
Dim res = context.Songs _
.SelectMany(Function(song) song.Artists, Function(s, a) New With
{.SongTitle = s.SongTitle, _
.ArtistName = a.ArtistName, _
.Lyrics = s.Lyrics})
But I am having the result like below.
You will see Lucky is displayed twice in the table. I don't want that to happen. I just want to display it once but have join two singers in the Artist column. I tried to read tutorials and many forum posts but those tutorials don't get this complicated.
So how can i get change the query to return something like this?

I must write my answer with C#, hopefully you are able to translate it into VB.
I would do two changes:
First, simply use Select instead of SelectMany in this situation.
Second, introduce a named ViewModel instead of an anonymous type because it allows you to add a method or custom readonly property that will be helpful later.
The ViewModel would look like this:
public class SongViewModel
{
public string SongTitle { get; set; }
public string Lyrics { get; set; }
public IEnumerable<string> ArtistNames { get; set; }
public string ArtistNamesString
{
get { return string.Join(", ", ArtistNames); }
}
}
Then you can use this query:
var res = context.Songs.Select(s => new SongViewModel
{
SongTitle = s.SongTitle,
Lyrics = s.Lyrics,
ArtistNames = s.Artists.Select(a => a.ArtistName)
});
Now, to list the result, you can use a loop like this (example with console output):
foreach (var item in res)
{
Console.WriteLine(string.Format("{0} {1} {2}",
item.SongTitle, item.Lyrics, item.ArtistNamesString);
}
This lists each song only once and the artist names are displayed as a comma separated list.

Related

Dapper question. Getting values from returned object

Just started learning Dapper. I have an ADO.NET background. Using a demo I downloaded, I can insert/delete data from a webform into a MySql table just fine. This, however, I have searched all morning on.
In retrieving a single row from the db by ID, it doesn't return a LIST<>, it seems to be just an object (using code from the demo I downloaded). The query works, I get the object back. It has the fields: "ProductID, Description and Price".
The only way I could get the values to those three fields was like this:
System.Reflection.PropertyInfo pi = Product.GetType().GetProperty("ProductID");
System.Reflection.PropertyInfo desc = Product.GetType().GetProperty("Description");
System.Reflection.PropertyInfo price = Product.GetType().GetProperty("Price");
int _ProductID = (int)(pi.GetValue(Product, null));
string _Description = (string)(desc.GetValue(Product, null));
decimal _Price = (decimal)(price.GetValue(Product, null));
This works and gets the correct values for the three fields.
I'm used to looping through DataTables, but I just think there is probably a better way to get those values.
Is this the correct way to do this or am I missing something? I did actually read documentation and mess with this all morning before asking, too.
Some of the things I looked at seem to be very complex. I thought Dapper was supposed to simplify things.
OK, Thanks Marc. It was difficult for me to see what was supposed to be in the Dapper class files and what was supposed to be in my code behind. The original demo way of getting a product by ID had the query as .FirstOrDefault();
I changed everything to return a List<> and it all worked. I'm sure my ADO.NET is showing, but this works. In Dapper class files:
public List<Product> ProductAsList(int Id)
{
return this._db.Query<Product>("SELECT * FROM Cart_product WHERE ProductID=#Id", new { Id = Id }).**ToList()**;
}
This is just getting one row that matched the ProductID.
In page codebehind:
protected void CartItemAdd(string ProductId) // passing it the selected ProductID
{
var results = cartservice.ProductAsList(Convert.ToInt32(ProductId));
// returns that one row using Dapper ProductAsList(ProductId)
int _ProductId = 0;
string Description = string.Empty;
decimal Price = 0;
// Loop through the list and get the value of each item:
foreach (Product obj in results)
{
_ProductId = obj.ProductID;
Description = obj.Description;
Price = obj.Price;
}
// Using Dapper to insert the selected product into the shopping cart (table):
String UserName = "jbanks";
cartitem = new CartItem();
cartitem.ProductID = _ProductId;
cartitem.Quantity = 1;
cartitem.Description = Description;
cartitem.Price = Price;
cartitem.Created = DateTime.Now;
cartitem.CreatedBy = UserName;
result = cartservice.AddCartItem(cartitem);
if (result)
{
lblMessage.Text = string.Empty;
lblMessage.Text = "Successfully added a cart item";
}
}
}
It does indeed look up the product from one table and insert a selected item into another table.
Thanks again!
The main Query<T> API returns an IEnumerable<T>, which often will be a List<T>; the AsList<T>() extension method can get it back to a list without a copy, but either way: they are just T, for whatever T you asked for. If you asked for Query<Product>, then: they should be Product instances:
var results = connection.Query<Product>(someSql, someArgs); // perhaps .AsList()
foreach (Product obj in results) { // "var obj" would be fine here too
// now just use obj.ProductID, obj.Description and obj.Price
}
If that didn't work: check that you used the <T> version of Query. There is a non-generic variant too, which returns dynamic. Frankly, you should almost always use the <T> version.
Note: I'm assuming that somewhere you have something like
class Product {
public int ProductID {get;set;}
public string Description {get;set;}
public decimal Price {get;set;}
}

How to use JsonConvert.SerializeObject with formatting to add brackets only around CSV values

I have an object that, when converted to a JSON string using the JsonConvert.SerializeObject method, will look like this:
{"01":{"CompanyName":"Hertz","Cars":"Ford, BMW, Fiat"},
"02":{"CompanyName":"Avis","Cars":"Dodge, Nash, Buick"}}
How can I use the Formatting parameter to make the result look like this:
{"01":{"CompanyName":"Hertz","Cars":["Ford", "BMW", "Fiat"]},
"02":{"CompanyName":"Avis","Cars":["Dodge", "Nash", "Buick"]}}
As #dbc mentioned in the comments, you cannot use the Formatting parameter of JsonConvert.SerializeObject to affect whether a particular value in the JSON is surrounded by square brackets or not. The Formatting parameter only controls whether or not Json.Net will add indenting and line breaks to the JSON output to make it easier to read by a human.
In JSON, square brackets are used to denote an array of values, as opposed to a single value. So, if you want to add square brackets for a particular property, the easiest way to do that is to change how that property is declared in your class such that it correspondingly represents an array (or list).
Based on your original JSON, I'm assuming you have a class which looks like this:
public class Company
{
public string CompanyName { get; set; }
public string Cars { get; set; }
}
...and you are creating your JSON something like this:
var results = new Dictionary<string, Company>();
results.Add("01", new Company
{
CompanyName = "Hertz",
Cars = "Ford, BMW, Fiat"
});
results.Add("02", new Company
{
CompanyName = "Avis",
Cars = "Dodge, Nash, Buick"
});
string json = JsonConvert.SerializeObject(results);
To get square brackets in the JSON, change the type of your Cars property from string to List<string>:
public class Company
{
public string CompanyName { get; set; }
public List<string> Cars { get; set; }
}
Of course, you will also need to make a corresponding change to the code which populates the results:
var results = new Dictionary<string, Company>();
results.Add("01", new Company
{
CompanyName = "Hertz",
Cars = new List<string> { "Ford", "BMW", "Fiat" }
});
results.Add("02", new Company
{
CompanyName = "Avis",
Cars = new List<string> { "Dodge", "Nash", "Buick" }
});
string json = JsonConvert.SerializeObject(results);
Here is a short demo: https://dotnetfiddle.net/lgg9jk

LINQ, select one item from one table, multiple items from another table

I am fairly new to using LINQ and are now trying to build a LINQ question I do not quite manage to solve.
I would like to ask a question to a database, where I want to bring back single rows from a few tables, but a list of rows from other tables.
See code below too see what I am trying to do:
public DB.store store { get; set; }
public List<DB.gallery_image> images { get; set; }
public List<DB.product> products { get; set; }
public static List<Store> getStoreInfo()
{
DBDataContext db = new DBDataContext();
var _dataToGet = from _store in db.stores
select new Store
{
store = _store,
images = (from a in db.gallery_images
where a.albumID == _store.storeID
select a).ToList(),
products = (from p in db.products
where p.storeID = _store.storeID).ToList()
};
return _dataToGet.ToList();
}
So I just want one row from "store" table, but a list from "images" and "product" tables.
The code above works fine, but is slow as hell.
I don't have any problems to select data from multiple tables as long as there is only one (or none) row per table, but when it is a list I'm having problem...
If I were you I would use deferred execution rather than materializing the queries with a call to ToList. I would change the data type of images and products to IEnumerable<> instead of List<>. Then I would not call ToList in the sub-queries because this results in a roundtrip to the database, hence, depending on how many stores you have it could turn into an extremely slow query.
You should see a performance gain here...
public DB.store store { get; set; }
public IEnumerable<DB.gallery_image> images { get; set; }
public IEnumerable<DB.product> products { get; set; }
public static List<Store> getStoreInfo()
{
DBDataContext db = new DBDataContext();
var _dataToGet = from _store in db.stores
select new Store
{
store = _store,
images = (from a in db.gallery_images
where a.albumID == _store.storeID
select a),
products = (from p in db.products
where p.storeID = _store.storeID)
};
return _dataToGet.ToList();
}

Import two or multiple class models to a single controller on ASP.NET

I'm very new to ASP.NET, but I know a little programming in Java. I want to use a ZIP code to query a database which will return a string, then use that string to query another database. I wanted to do this on the same control model. I thought it would be easy, and it sounds pretty easy.
When I created the controller, I put the model class of the first database, and, so far, I've gotten as far as querying the first database, but now that I have the string I want to query a second database through the DBEntities.
This displays an error saying:
> The model item passed into the dictionary is of type
> 'System.Collections.Generic.List`1[FinalBallot.Models.AgainCandidate]',
> but this dictionary requires a model item of type
> 'System.Collections.Generic.IEnumerable`1[FinalBallot.Models.ZipTable]'.
Is there a way to solve this in an easy way?
public class Default1Controller : Controller
{
private CandidatesDBEntities db = new CandidatesDBEntities();
public string districString = "";
//
// GET: /Default1/
public ViewResult Index(string searchString)
{
var queryZip = from s in db.ZipTables select s;
var queryCandidates = from s1 in db.AgainCandidates select s1;
double sT = 0;
//method so it doesnt display the whole db
if (String.IsNullOrEmpty(searchString))
{
queryZip = queryZip.Where(s => s.ZipL.Equals(0));
}
if (!String.IsNullOrEmpty(searchString))
{
sT = double.Parse(searchString);
queryZip = queryZip.Where(s => s.ZipL.Equals(sT));
try
{
districString = queryZip.ToList().ElementAt(0).District;
}
catch
{
}
if (!String.IsNullOrEmpty(districString))
{
queryCandidates = queryCandidates.Where(s1 => s1.District.Equals(districString));
}
}
return View(queryCandidates.ToList());
}
In your view, did you specify the model to be IEnumerable<ZipTable>? The model that you're passing to your view is IEnumerable<AgainCandidate>, so you would get an error if you specified your model as something else. You'd need to change the model in your view to be IEnumerable<AgainCandidate>.
UPDATE:
Based on your revised explanation, you can do a couple things:
1) create a "ViewModel" that has two properties for each of your collections you want to display on the page like so:
public class MyViewModel
{
IEnumerable<ZipTable> Zips { get; set; }
IEnumerable<AgainCandidate> Candidates { get; set; }
}
Instantiate that in your action method and return that as your model. This would be my preferred approach.
2) Stash your two collections in the ViewData bag in your action method:
ViewData["Zips"] = queryZip.ToList();
ViewData["Candidates"] = queryCandidates.ToList();
return View(ViewData);
You can pull this data in your view like this:
#foreach (var zip in ViewData["Zips"] as IEnumerable<ZipTable>)
{
...
}

RavenDB query by index and DateTime

I'm quite new to RavenDB so sorry if my question sounds stupid. I have a class which contains a DateTime property. I store instances of this class in RavenDB. I have defined index the following way:
from doc in docs.Orders
from docItemsItem in ((IEnumerable<dynamic>)doc.Items).DefaultIfEmpty()
select new { Items_Price_Amount = docItemsItem.Price.Amount, Items_Quantity = docItemsItem.Quantity, Date = doc.Date }
http://dl.dropbox.com/u/3055964/Capture.GIF <-- here's a screenshot
Here's class definition:
public class Order
{
public DateTime Date { get; set; }
public IList<OrderItem> Items { get; set; }
public string CustomerId { get; set; }
public Order()
{
Items = new List<OrderItem>();
}
}
Now, when I try to query RavenDB with the index shown above, the query yields no result at all.
var orders = session.Query<Order>("OrdersIndex").Where(o => o.Date > DateTime.Now).ToList(); // orders.Count == 0
If I omit the index from query, like this:
var orders = session.Query<Order>().Where(o => o.Date > DateTime.Now).ToList(); // orders.Count == 128
a temporary index is created and eveything works as expected.
Does anyone has any idea what's wrong with my query?
Thanks.
UPDATE
Allright, I removed Fields Date, Items,Price,Amount and Items,Quantity via management studio (shown in screenshot), and now the query works fine. Anyone any idea why? What's the purpose to define those fields explicitly?
Check that the date in the Index is stored as Index(x => x.Date, FieldIndexing.Default).
I had it set to FieldIndexing.Analysed, and wasn't getting the correct results back.
I need to read up more on the difference between the different FieldIndexing options :)

Resources