creating a list of Id's from database. C# - c#

Im trying to generate a random ID number so when the user clicks a button it redirects to a random artist. I was able to do it and it works quite well with the code below.
var artists = _context.Artists;
var totalArtists = artists.Count();
Random randomArtistNumber = new Random();
int randomArtistID = randomArtistNumber.Next(1, totalArtists + 1);
if (button.Equals("btnArtist"))
{
return RedirectToAction("Details", "ArtistModels", new { id = randomArtistID.ToString() });
}
The problem here is that if the user deletes an artist then im going to have an ID number that no longer exists in the random list created above. How would I go about creating a List<> of all ArtistID's in the Artist Table? It would be much better to just pick a random number from within a list of active ID's
ArtistModel below
public class ArtistModel
{
[Key]
public int ArtistID { get; set; }
public string ArtistName { get; set; }
public string CountryOfOrigin { get; set; }
public int SubgenreID { get; set; }
public SubgenreModel Subgenre { get; set; }
public ICollection<AlbumModel> Albums { get; set; }
}

I'm guessing you are using Entity Framework?
You should be able to get a list of ids from your artists by doing something like this.
var artistIds = artists.Select(a => a.Id).ToList();
To do that correctly you'd need to ensure that you were always loading all artists from the context.
Might be better if you random function had a limit, like maybe randomly select from the first 200 artists or something.

Since you already have the artists, what you can actually do is get a random artist, it wouldnt be much different from what you have
Something like:
int randomArtistIndex = randomArtistNumber.Next(1, totalArtists);
var artist = artists[randomArtistIndex]
and then
return RedirectToAction("Details", "ArtistModels", new { id = artist.ArtistID.ToString() });

Related

How can I update specific element in List<T>?

I am writing a C# console application where I have a class Customer as following:
public class Customers
{
public string Name { get; set; }
public string UserID { get; set; }
public int Pin { get; set; }
public int AccountNo { get; set; }
public string AccountType { get; set; }
public int Balance { get; set; }
public string Status { get; set; }
}
I am storing the customer data in text file as below ("Customers.txt"):
[
{"Name":"Jack Willson","UserID":"Jack21","Pin":12345,"AccountNo":1,"AccountType":"Savings","Balance":2000,"Status":"Active"},
{"Name":"Mary Poppins","UserID":"mary8912","Pin":67890,"AccountNo":2,"AccountType":"Savings","Balance":4567,"Status":"Active"},
{"Name":"Harry Potter","UserID":"Harry45","Pin":12345,"AccountNo":4,"AccountType":"Savings","Balance":12000,"Status":"Active"}
]
I am reading this file as :
List<Customers> list = ReadFile<Customers>("Customers.txt");
I wish to update a specific customer field based on user input. If user leaves the field input blank then the field information remains unchanged.
The program will ask user to enter the AccountNo for which he wishes to update an information.
Then program will display each property like UserID, AccountType, Status. If the user doesnot enter any input for any of the property then information remains unchanged.
I was trying to save the input from user in new Customer() object but couldn't proceed to compare it or save it in List<customers> where i am storing the data from text file.
How can I achieve this?
Something like this should do the trick.
var accountNum = 2; //I'm going to assume you get this from Console.ReadLine() and validate it.
//Find the customer with that account number...
var query = list.Where(c => c.AccountNo == accountNum);
if (!query.Any()
Console.WriteLine("Customer not found.");
var customer = query.First();
//Now you can manipulate the customer object as you please
//In your situation I think you want to loop through the properties they can change and set them.
customer.Name = "Some new name";
customer.Pin = 1234;
//Save the updated information back to the text file however you currently do this
Hopefully this helps :)
You can look into this solution using Linq:
int acNo = ReadAccountNumber();
IEnumerable<Customers> results = myList.Where(c => c.AccountNo == acNo);
if you are sure that AccountNo are unique then results will hold just one element. You can update the first element if that exists.
Customers cust = myList.First(c => c.AccountNo == acNo);
cust?.Name = newName;

What's the correct way to reference tables using Code First with EF.Core for searching efficiently

Fairly new to EF.Core and I'm having some issues as my tables start getting more complex. Here's an example of what I have defined for my classes. Note ... there are many more columns and tables than what I have defined below. I've paired them down for brevity.
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Active { get; set; }
}
Followed by
public class JournalEntry
{
public int Id { get; set; }
public int UserId { get; set; }
public string Details { get; set; }
public DateTime DateEntered { get; set; }
public virtual User User { get; set; }
}
I want to be able to issue the following query and INCLUDE the User Table so that I can then populate a ViewModel with columns from the User Table without having to do another lookup and also to sort the data while retrieving it:
public IQueryable<JournalEntry> GetByUser(int userId)
{
return _DbContext.JournalEntries.Where(j => j.UserId == userId)
.Include(u => u.User)
.OrderBy(u=> u.User.FirstName)
.ThenBy(j => j.DateEntered);
}
My controller would then have something similar to the following:
public IActionResult List(int userId)
{
var journalEntries = new _dbRepository.GetByUser(userId);
var myViewModel = new MyViewModel();
myViewModel.UserName = ($"{journalEntries.User.FirstName} {journalEntries.User.LastName}");
myViewModel.Entries = journalEntries;
etc ....
return View(myViewModel);
}
I'm loading the user's first and last name in the View Model and whatever other attributes from the various tables that are referenced. The problem that I'm having is that I'm getting errors on the Migration creation "Foreign key constraint may cause cycle or multiple cascade paths." And of course, if I remove the line reading public virtual User User { get; set; } from the JournalEntry class then the problem goes away (as one would expect).
I believe that the way I'm doing the models is incorrect. What would be the recommended way that I should code these models? I've heard of "lazy loading". Is that what I should be moving towards?
Thanks a bunch.
--- Val
Your query returns an IQueryable<JournalEntry> not a JournalEntry.
Change the code to get the user details from the first object:
var myViewModel.UserName = ($"{journalEntries.First().User.FirstName} {journalEntries.First().User.LastName}");
In the line above I'm calling First() on your journal entries collection and that would have a User. Then I can access FirstName and LastName.
Also, don't bother with LazyLoading since you are learning. It could cause select n+1 issues if used incorrectly

Elegant way to map some properties in one List based on Id from another List in C#

I have a User List and Branch List. One Branch can have multiple User. I need to assign few properties in User from Branch based on BranchId. Here are my classes.
User:
public class User
{
public Guid Id { get; set; }
public bool Active { get; set; }
public Guid BranchId { get; set; }
public string BranchName { get; set; }
}
Branch:
public class Branch
{
public Guid Id { get; set; }
public bool Active { get; set; }
public string BranchName { get; set; }
}
Here is what I have tried,
foreach (var user in vm)
{
var branch = branches.Single(b => b.Id.Equals(user.BranchId));
user.BranchName = branch.BranchName;
}
Right now this works, but I feel this as not a good solution as to iterate users and get branch everytime for that user and assign that. May be multiple users in the list can have same BranchId in that case it happens like I'm reading the same branch again and again. Any better solutions using LINQ or this can be improved in any ways? please assist.
Well, you could create a dictionary mapping from branch ID to brand name:
var branchIdToBrandName = branches.ToDictionary(b => b.Id, b => b.BrandName);
foreach (var user in users)
{
user.BrandName = branchIdToBranchName[user.BranchId];
}
That's assuming there's exactly one branch per branch ID, and that every user's branch ID is covered. If you need to do the same for other properties, you'd probably want to just map branch ID to branch, rather than straight to brand name, e.g.:
var branchIdToBranch = branches.ToDictionary(branch => branch.Id);
foreach (var user in users)
{
user.BrandName = branchIdToBranch[user.BranchId].BrandName;
}

Creating custom linq column names in list

We are returning a generic List to a GridView, which then auto generates columns to show a report:
//Generate List
List<Stock> allStock = blStock_Collection.getAll();
//export custom view for report datagrid
return (from myStock in allStock
select new
{
myStock.Category,
myStock.Description,
myLowStock.UnitPrice,
myLowStock.CurrentQuantity
});
Our client has asked that we now provide multi-lingual support on our site (English & Polish), specifically the column headers on grids. We would therefore need to get rid of the auto generate option on all our data grids, and add in the translations manually for each column.
I was wondering is there a way to do this when generating the datasource, which would save us a hell of a lot of time, e.g. changing this line:
myStock.Category,
to something like:
languagePack.Category = myStock.Category,
This is of course throwing a 'Invalid anonymous type member declarator' error. Any advice?
Maybe I misread something, but if you just want to put your language into it (LanguagePack is just a class holding your values):
class LanguagePack
{
public int Category { get; set; }
public string Description { get; set; }
public decimal UnitPrice { get; set; }
public int CurrentQuantity { get; set; }
public int LanguageName { get; set; }
}
return (from myStock in allStock
select new LanguagePack
{
Category = myStock.Category,
Description = myStock.Description,
UnitPrice = myLowStock.UnitPrice,
CurrentQuantity = myLowStock.CurrentQuantity,
LanguageName = "Polish" // or english or whatever... maybe LanguageId or something corresponding to your model
});

Database items getting deleted when referencing them multiple times

I am creating a simple blogging application to get .NET MVC 4 down and I am having a problem. Everything works except for when I try to tag a blog using an array of strings for each blog like so:
public class BlogEntry
{
public List<Comment> BlogComments { get; set; }
public virtual List<String> RawTags { get; set; }
public virtual List<Tag> BlogTags { get; set; }
public virtual User Author { get; set; }
public int AuthorId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime DatePosted { get; set; }
[Key]
public int Id { get; set; }
public bool IsAcceptingComments { get; set; }
public bool IsVisible { get; set; }
public DateTime LastEdited { get; set; }
}
public class Tag
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int RefCount { get; set; }
}
Upon creating a blog and tagging it, I save tags into the BlogEntry model using this:
[HttpPost]
public int Create(string data)
{
if (data != null)
{
BlogEntry newBlog = JsonConvert.DeserializeObject<BlogEntry>(data);
newBlog.Author = Session["user"] as User;
newBlog.AuthorId = newBlog.Author.Id;
newBlog.IsVisible = true;
newBlog.IsAcceptingComments = true;
newBlog.LastEdited = DateTime.Now;
newBlog.DatePosted = DateTime.Now;
newBlog.BlogTags = new List<Tag>();
foreach (String s in newBlog.RawTags)
{
// First check to see if the tag already exists
Tag check = Db.Tags.Where(m => m.Name == s).FirstOrDefault();
if (check != null)
{
check.RefCount++;
newBlog.BlogTags.Add(check);
Db.Tags.Attach(check);
Db.Entry(check).State = System.Data.Entity.EntityState.Modified;
Db.SaveChanges();
}
else
{
// Create a new tag
Tag newTag = new Tag();
newTag.Name = s;
newTag.RefCount = 1;
newBlog.BlogTags.Add(newTag);
Db.Tags.Add(newTag);
}
}
Db.BlogEntries.Add(newBlog);
Db.SaveChanges();
return newBlog.Id;
}
return -1;
}
First I do a check to see if a tag already exists.. If it does, I try to add the same tag, check to the newBlog object. I would have thought that this would just save a reference to this Tag object in the DbSet, however, if I create multiple blogs posts with the tag "html" and then run a query to see what blogs have the html tag, only the most recently tagged blog retains this value.... What can I do so that I can have multiple BlogEntry objects with the same Tag object in the database?
I don't have my dev machine in front of me right now, so this is just a guess, but I figure it's better than making you wait until tomorrow...
I don't think you need the last 3 lines in your if(check!=null) and in fact, I wonder if they aren't messing you up:
Db.Tags.Attach(check);
Db.Entry(check).State = System.Data.Entity.EntityState.Modified;
Db.SaveChanges();
You shouldn't need to attach because you got it from the Db object already, so it should already be being tracked. This means you don't need to change the state and as for the SaveChanges, you are going to do that below.
And now another disclaimer: I've done some work with Entity Framework (version 6, if you want to know), but not with MVC, so it may be different, but my understanding is that it is better to create a new DbContext for each set of instructions, rather than having a class variable that just tracks running changes. I'm not sure if that is what you are doing or not, but it sort of looks that way from this code sample. Assuming that is relevant in MVC, you may consider creating a new DbContext (Db) at the top of your create method.
Let me know how it goes--if this doesn't help, I'll delete this answer.
First you would have to update the Tag class so that it can track its registered blog entries itself. Here the BlogEntry and Tag classes have a many-to-many relationship. So the Tag class would look like below:
public class Tag
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int RefCount { get; set; }
public virtual List<BlogEntry> BlogEntries { get; set; } // MODIFICATION
}
Now you have to add the blog entry to all of its tags for back referencing to meet your query in an easy way. Look for the modifications I have made in the for-loop below:
foreach (String s in newBlog.RawTags)
{
// First check to see if the tag already exists
Tag check = Db.Tags.Where(m => m.Name == s).FirstOrDefault();
if (check != null)
{
check.RefCount++;
check.BlogEntries.Add(newBlog); // MODIFICATION
newBlog.BlogTags.Add(check);
Db.Tags.Attach(check);
Db.Entry(check).State = System.Data.Entity.EntityState.Modified;
Db.SaveChanges();
}
else
{
// Create a new tag
Tag newTag = new Tag();
newTag.Name = s;
newTag.RefCount = 1;
newTag.BlogEntries = new List<BlogEntry>(); // MODIFICATION
newTag.BlogEntries.Add(newBlog); // MODIFICATION
newBlog.BlogTags.Add(newTag);
Db.Tags.Add(newTag);
}
}
To see what blogs have the html tag, you just have to query on the Tag class and search through the BlogEntries to get the desired blogs. Good luck!

Categories