"Property is not defined" runtime error in Linq query - c#

I'm having an issue using .Net/EF Core, a select statement in one of my view model constructors is returning the following error at Runtime: Property 'System.String Name' is not defined for type 'Microsoft.EntityFrameworkCore.Storage.ValueBuffer'.
I've tried commenting out the line I suspected was causing the issue and have narrowed it down to that, but I can't figure out what's wrong with what I'm doing.
The following is the expression that's causing the error:
playerholder = players.Select(m => new PlayerListItem_PDMI
{
PlayerID = m.PlayerID,
FirstName = m.FirstName,
LastName = m.LastName,
Rating = m.Rating,
Assigned = m.TeamAssignments.Where(n => n.Team.League == league && n.SeasonID == SeasonID).Count() > 0,
TeamName = (m.TeamAssignments.Where(n => n.Team.League == league && n.SeasonID == SeasonID).Count() > 0 ? m.TeamAssignments.Where(n => n.Team.League == league && n.SeasonID == SeasonID).First().Team.Name : "Not Assigned")
}).ToList();
and the specific line is
TeamName = (m.TeamAssignments.Where(n => n.Team.League == league && n.SeasonID == SeasonID).Count() > 0 ? m.TeamAssignments.Where(n => n.Team.League == league && n.SeasonID == SeasonID).First().Team.Name : "Not Assigned")
What am I missing here?
Note: The database being queried is completely empty. The check for records should return false and make the ternary expression return "Not Assigned"
Edit:
The TeamAssignment class is as follows:
public class TeamAssignment : IModel
{
public TeamAssignment()
{
}
public TeamAssignment(WWAHLContext db)
{
Season = db.Seasons.Last();
}
[Key]
public int ID { get; set; }
public int PlayerID { get; set; }
public int SeasonID { get; set; }
public int TeamID { get; set; }
public int PlayerNumber { get; set; }
public Position Position { get; set; }
[ForeignKey("PlayerID")]
public virtual Player Player { get; set; }
[ForeignKey("TeamID")]
public virtual Team Team { get; set; }
[ForeignKey("SeasonID")]
public virtual Season Season { get; set; }
}
and the Team class is this:
public class Team : IModel
{
public int TeamID { get; set; }
public int SeasonID { get; set; }
public League League { get; set; }
public string Name { get; set; }
public virtual TeamStats TeamStats { get; set; }
public virtual TeamCarousel Carousel { get; set; }
public virtual ICollection<GameDetails> Games { get; set; }
public virtual ICollection<CurrentTeam> CurrentPlayers { get; set; }
[NotMapped]
public virtual IEnumerable<Penalty> Penalties => Games.SelectMany(m => m.Penalties);
}

I resolved this by using joins as follows (edited for left outer join):
playerholder = players.GroupJoin(db.TeamAssignments, player => player.PlayerID, assignment => assignment.PlayerID, (player, assignment) => new
{
PlayerID = player.PlayerID,
FirstName = player.FirstName,
LastName = player.LastName,
Rating = player.Rating,
NameLeague = assignment.Where(m => m.SeasonID == seasonId).Join(db.Teams, assign => assign.TeamID, team => team.TeamID, (assign, team) => new
{
League = team.League,
Name = team.Name
}).Where(m => m.League == league)
}).Select(m => new PlayerListItem_PDMI
{
PlayerID = m.PlayerID,
FirstName = m.FirstName,
LastName = m.LastName,
Rating = m.Rating,
Assigned = m.NameLeague.Any(),
TeamName = (m.NameLeague.Any() ? m.NameLeague.First().Name : "Not Assigned")
}).ToList();

First, let's make a sensible query out of this.....
playerholder = from m in players
let team = m.TeamAssignments.FirstOrDefault(n => n.Team.League == league && n.SeasonID == SeasonID)
select new PlayerListItem_PDMI
{
PlayerID = m.PlayerID,
FirstName = m.FirstName,
LastName = m.LastName,
Rating = m.Rating,
Assigned = team != null,
TeamName = team != null ? team.Name : "Not Assigned")
}).ToList();
Just try that first to see if it works, or gives the same error. Since the error references ValueBuffer, I'm going to guess that the thing .Name is attached to is not a Team object --- possibility due to a misplace parenthesis in that mess...

Related

C# and MongoDriver - How to get values from foreign collection (aggregate + lookup)?

I've got a problem with getting values from foreign collection in C#.
In this case I can easily get values from list:
var gamesList = gamesCollection.Find(_ => true).ToList();
foreach (var item in gamesList)
{
Console.WriteLine($"{item.Title}");
}
But when I'm using aggregate with lookup function, I can not access to values from foreign collection.
Here are my two collections which I try to join:
public class GameModel
{
[BsonId]
public ObjectId Id { get; set; }
public string Title { get; set; }
public List<String> Type { get; set; }
public string GameMode { get; set; }
public List<String> Platform { get; set; }
public string Production { get; set; }
}
public class FavouriteGameModel
{
[BsonId]
public ObjectId Id { get; set; }
public ObjectId UserID { get; set; }
public ObjectId GameID { get; set; }
}
And here's the part of problematic code:
var joinedFavGamesList = favouriteGamesCollection.Aggregate().Match(x => x.UserID == loggedUser[0].Id).//ToList();
Lookup("Games", "GameID", "_id", #as: ("myAlias")).
Project(
new BsonDocument { { "_id", 0 }, { "myAlias.Title", 1 } }
).ToList();
Is there any way to invoke to myAlias.Title? I want only this value to display, but i get:
{ "myAlias" : [{ "Title" : "Some Game" }] }
I will be greatful if someone could look at this and tell me what I'm doing wrong. Thanks
my choice would be to join/lookup using the AsQueryable interface like so:
var favGames = favCollection.AsQueryable()
.Where(fg=> fg.UserID== "xxxxxxxxxxx")
.Join(gameCollection.AsQueryable(), //foreign collection
fg => fg.GameID, //local field
gm => gm.ID, //foreign field
(fg, gm) => new { gm.Title }) //projection
.ToList();
with aggregate interface:
public class JoinedGameModel
{
public GameModel[] Results { get; set; }
}
var favGames = favGameCollection.Aggregate()
.Match(fg => fg.UserID == "xxxxxxxxxxxx")
.Lookup<FavouriteGameModel, GameModel, JoinedGameModel>(
gameCollection,
fg => fg.GameID,
gm => gm.ID,
jgm => jgm.Results)
.ReplaceRoot(jgm => jgm.Results[0])
.Project(gm => new { gm.Title })
.ToList();

Linq2Entities query to find records given mix-max range

I have those two entities:
public class Ticket
{
public int Id { get; set; }
public int ScheduleId { get; set; }
public int SeatId { get; set; }
[DataType(DataType.Date)]
[Column(TypeName = "Date")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ForDate { get; set; }
public Seat Seat { get; set; }
public Schedule Schedule { get; set; }
public ICollection<TicketStop> TicketStops { get; set; }
}
public class TicketStop
{
public int Id { get; set; }
public int TicketId { get; set; }
public int LineStopStationId { get; set; }
public Ticket Ticket { get; set; }
public LineStopStation LineStopStation { get; set; }
}
public class LineStopStation
{
public int Id { get; set; }
public int LineId { get; set; }
public int StopId { get; set; }
public int Order { get; set; }
public bool IsLastStop { get; set; }
public Line Line { get; set; }
public Stop Stop { get; set; }
}
The business case is that I am implementing a bus ticket reservation system (for learning purposes mostly) and I want to find overlapping tickets.
The combination of LineId + ScheduleId + ForDate identifies uniquely that that a ticket is for certain bus at a certain date and departure time.
The problem I have is to identify, given starting and end location whether or not two tickets overlap for one or more stops.
The LineStopStation entity holds information about the StopId and the Order in which it is visited during the bus trip. So basically, overlapping tickets will have same Order number at some point (unless it is the last stop, which means that the passenger is leaving the bus).
So what I have is LineId, ScheduleId, StartId and EndId where starId and endId correspond to LineStopStation.StopId so eventually I am able to get the Order out of them like this.
int startStationOrder = _context.LineStopStations
.First(l => l.LineId == lineId && l.StopId == startId).Order;
int endStationOrder = _context.LineStopStations
.First(l => l.LineId == lineId && l.StopId == endId).Order;
So I am pretty convinced that having all this information I should be able to find if in TicketStop table I have ticket which overlaps with the data in question. TicketStop works this way - if someone bought a ticket for 3 stops I will have three records there with the same TicketId and three different LineStopStationId.
I feel that this question got a bigger than needed. So basically I have this:
public List<Seat> GetAvailableSeats(int lineId, int scheduleId, int startId, int endId, DateTime forDate)
{
int startStationOrder = _context.LineStopStations
.First(l => l.LineId == lineId && l.StopId == startId).Order;
int endStationOrder = _context.LineStopStations
.First(l => l.LineId == lineId && l.StopId == endId).Order;
var reservedSeats = _context.TicketStops
.Where(t => t.Ticket.ScheduleId == scheduleId)
.Where(t => t.Ticket.ForDate == forDate)
//basically I don't know how to proceed here.
//In pseudo code it should be something like:
.Where(t => t.Min(Order) >= endStationOrder || t.Max(Order) <= startStationOrder
}
But obs. this is not how LINQ works. So how can I find all tickets where this range overlaps?
Without an in-depth analysis of your model, perhaps something like this could give you an idea?
var reservedSeats = _context.TicketStops
.GroupBy(t => new { t.Ticket.ScheduleId, t.Ticket.ForDate })
.Where(tg => tg.Key == new { ScheduleId = scheduleId, ForDate = forDate })
.Where(tg => tg.Min(t => t.LineStopStation.Order) >= endStationOrder || tg.Max(t => t.LineStopStation.Order) <= startStationOrder);
You could also filter first and do an empty GroupBy:
var reservedSeats = _context.TicketStops
.Where(t => t.Ticket.ScheduleId == scheduleId && t.Ticket.ForDate == forDate)
.GroupBy(t => 1)
.Where(tg => tg.Min(t => t.LineStopStation.Order) >= endStationOrder || tg.Max(t => t.LineStopStation.Order) <= startStationOrder);
To return all the SeatIds, you just need to select them from the group.
var reservedSeats = _context.TicketStops
.Where(t => t.Ticket.ScheduleId == scheduleId && t.Ticket.ForDate == forDate)
.GroupBy(t => 1)
.Where(tg => tg.Min(t => t.LineStopStation.Order) >= endStationOrder || tg.Max(t => t.LineStopStation.Order) <= startStationOrder);
.SelectMany(tg => tg.Select(t => t.Ticket.SeatId));

Entity Framework trying to use methods to create joins help needed

I could really use some help, I have the following SQL query that works and have been trying to convert it to Entity Framework to no avail. I am using the Methods rather than the other option, I think that is how you say it.
Anyway the SQL is
SELECT c.ACCOUNTID,
c.TOACCOUNTID,
fa.ACCOUNTNAME, ta.ACCOUNTNAME,
p.PAYEENAME
FROM checking AS c
LEFT JOIN
ACCOUNT AS fa ON c.ACCOUNTID = fa.ACCOUNTID
LEFT JOIN
ACCOUNT AS ta ON c.TOACCOUNTID = ta.ACCOUNTID
LEFT JOIN
PAYEE AS p ON c.PAYEEID = p.PAYEEID
WHERE c.ACCOUNTID == 1 OR
c.TOACCOUNTID == 1;
So far I have managed to get it this far.
var checking =
db.Transactions
.Where(item => item.ACCOUNTID == LookupAccount || item.TOACCOUNTID == LookupAccount)
.GroupJoin(db.PAYEE,
transaction => transaction.PAYEEID,
payee => payee.PAYEEID,
(check, payee) => new { Payee = payee }
).SelectMany(
transaction => transaction.Payee.DefaultIfEmpty(),
(transaction, payee) => new { Payee = payee })
.Select(item => new
{
ToAccount = item.ToAccount.AccountName
FromAccount = item.FromAccount.AccountName
Withdrawal = 0,
Deposit = 0,
Payee = item.Payee.PAYEENAME
}).ToList();
The issue I have now, is that I am not sure I understand how joins work in this manner, every time I try to get the 2 other joins in I end up falling flat on my face.
When I add this to the above code, the Payee section is out of whack and I don't understand why. I know it has to do with the select new {} section, I could really use some help understanding how this works.
.Join(db.ACCOUNT,
check => check.ACCOUNTID,
account => account.ACCOUNTID,
(check, fromaccount) => new { FromAccount = fromaccount }
)
Models
Transaction
public partial class Transaction
{
[Key]
public int Id { get; set; }
public int AccountID { get; set; }
public int? ToAccountId { get; set; }
public int PayeeId { get; set; }
[Column(TypeName = "numeric")]
public decimal TransAmount { get; set; }
[Column(TypeName = "numeric")]
public decimal ToTransAmount { get; set; }
public virtual Account Account { get; set; }
}
Account
public partial class Account
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Account()
{
Transaction = new HashSet<Transaction>();
}
[Key]
public int AccountId { get; set; }
[Required]
[StringLength(150)]
public string AccountName { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Transaction> Transaction { get; set; }
}
This won't work as a comment, but it might get you further. You have 2 FK in your tx class so you need 2 navigation properties:
public partial class Transaction
{
public int Id { get; set; } // Key by convention
public int FromAccountID { get; set; }
public virtual Account FromAccount { get; set; }
public int? ToAccountId { get; set; }
public virtual Account ToAccount { get; set; }
public int PayeeId { get; set; }
public virtual Payee Payee { get; set; }
public decimal TransAmount { get; set; }
public decimal ToTransAmount { get; set; }
}
public partial class Account
{
public Account()
{
Transaction = new HashSet<Transaction>();
}
public int AccountId { get; set; } // Key by convention
[Required]
[StringLength(150)]
public string AccountName { get; set; }
[InverseProperty("FromAccount")]
public virtual ICollection<Transaction> TransactionsFrom { get; set; }
[InverseProperty("ToAccount")]
public virtual ICollection<Transaction> TransactionsTo { get; set; }
}
Now your query becomes:
var checking =
db.Transactions
.Include(tx => tx.Payee)
.Include(tx => tx.FromAccount)
.Include(tx => tx.ToAccount)
.Where(tx => tx.FromAccountId == lookupAccountId || tx.ToAccountId == lookupAccountId)
.Select(tx => new
{
ToAccountName = tx.ToAccount.AccountName
FromAccountName = tx.FromAccount.AccountName
Withdrawal = tx.ToTransAmount,
Deposit = tx.TransAmount,
PayeeName = tx.Payee.PAYEENAME
}).ToList();
https://coding.abel.nu/2012/06/dont-use-linqs-join-navigate/
I have created this as an answer, for any one else who may come across this and is learning the syntax.
The reason I wasn't getting it to work was really down to a lack of how EF actually works when joining.
Transactions
.Join(Accounts,
tr => tr.AccountId,
ac => ac.AccountId,
(tr, ac) => new { Transaction = tr, ac})
.GroupJoin(Accounts,
tr => tr.Transaction.ToAccountId,
ta => ta.AccountId,
(tr, ta) => new { Transaction = tr.Transaction, Account = ta, FromAccount = tr.ac})
.SelectMany(
transaction => transaction.Account.DefaultIfEmpty()
,(transaction, account) => new { tt = transaction.Transaction, ToAccount = account, FromAccount = transaction.FromAccount}
)
.GroupJoin(Payees,
tr => tr.tt.PayeeId,
payee => payee.PAYEEID,
(tr, payee) => new { Transaction = tr, Payee = payee })
.SelectMany(
transaction => transaction.Payee.DefaultIfEmpty(),
(transaction, payee) => new {transaction = transaction.Transaction.tt, FromAccount = transaction.Transaction.FromAccount, ToAccount = transaction.Transaction.ToAccount, Payee = payee })
.Where (x=> x.FromAccount.AccountId == 1 || x.ToAccount.AccountId == 1)
.Select(item => new
{
item.transaction.Id,
TransDate = item.transaction.TRANSDATE,
Number = item.transaction.TransactionNumber,
Payee = item.Payee.PAYEENAME,
Status = item.transaction.Status,
Category = item.transaction.CategoryId,
FromAccount = item.FromAccount.AccountName,
ToAccount = item.ToAccount.AccountName,
Withdrawal = 0,
Deposit = 0,
Notes = item.transaction.Notes
})
The parts that I was not understanding was the relationship in code, from the joins and how the Selects then took over and created a hierarchy through the objects. I really wish that I could have used Navigation here, but from my understanding of Navigation's there needs to be a relationship between the columns and nearly all the fields being joined on here, can be null and therefore a foreign key would not satisfy the requirements for a Navigation.
I am still convinced there is a better way, but for now I have placed this here for others who are still learning and wish to see a working solution.

RavenDB: how to write queries on grandchildren?

Given the following C# code below, I am trying to retrieve all the continents having provinces whose cities include:
a city with Name and Address respectively set to "Aloma" and "123" and
another city with Name and Address respectively set to "Hemane" and "435".
public class Continent
{
public string Name { get; set; }
public List<Country> Countries{ get; set; }
}
public class Countries
{
public string Name { get; set; }
public List<Province> Provinces{ get; set; }
}
public class Province
{
public string Name { get; set; }
public List<Province> Cities { get; set; }
}
public class City
{
public string Name { get; set; }
public string Address { get; set; }
}
I have tried to use the query below, but it does not seem to work. Can you please help me?
Expression<Func<Province, bool>> matchCities = rec =>
(rec.Cities.Count(fi => fi.Name == "Aloma" && fi.Address== "123") > 0)
&& (rec.Cities.Count(fj => fj.Name == "Hemane" && fj.Address== "435") > 0);
Func<Province, bool> funcMatchCities= matchCities.Compile();
var results3 = session.Query<Continent>()
.Where(top => top.Countries.Any(ta => ta.Province.Any(
rec => funcMatchCities(rec))))
.OfType<Continent>()
.ToList();
You can query it like so:
var results3 = session.Query<Continent>()
.Where(top => top.Countries.Any(ta => ta.Province.Any(
rec =>
(rec.Fields.Any(fi => fi.Name == "Aloma" && fi.Address== "123") )
&& (rec.Fields.Any(fj => fj.Name == "Hemane" && fj.Address== "435") )))
.OfType<Continent>()
.ToList();
Note that you cannot Compile expression when you send them to the linq provider.

.NET MVC - Many-To-Many Relationship with Find or Create.- Duplicating records

I am trying to achieve a functionality very similar to what we have here on SO - The Tagging System. I enter tags, it looks if they exist - and if not, they are being created an associated with the post via a Join Table (Many-To-Many).
It works like this: The User enters the Tags, in a ", "-Seperated Value (The TagList). I split the TagList with a RegEx to extract the different tags - I try to look the tag up in the Database. And If it doesnt exist, I create it.
So far, this is what I have:
Recipe.cs
public class Recipe
{
[Key]
public int RecipeId { get; set; }
[Required]
public string Name { get; set; }
public string Subtitle { get; set; }
public int Serving { get; set; }
public string Instructions { get; set; }
public int PrepTime { get; set;}
public int CookingTime { get; set; }
public IList<Wine> Wines { get; set; }
public IList<Pairing> Pairings { get; set; }
public ICollection<UsedIngredient> UsedIngredients { get; set; }
[NotMapped]
public string TagList { get; set; }
public IList<Tag> Tags { get; set; }
}
Tag.cs
public class Tag
{
public int TagId { get; set; }
public string Name { get; set; }
public ICollection<Recipe> Recipes { get; set; }
}
The Join Table
CreateTable(
"dbo.TagRecipes",
c => new
{
Tag_TagId = c.Int(nullable: false),
Recipe_RecipeId = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.Tag_TagId, t.Recipe_RecipeId })
.ForeignKey("dbo.Tags", t => t.Tag_TagId, cascadeDelete: true)
.ForeignKey("dbo.Recipes", t => t.Recipe_RecipeId, cascadeDelete: true)
.Index(t => t.Tag_TagId)
.Index(t => t.Recipe_RecipeId);
TagRepo - FindOrCreate Method
public Tag FindOrCreateTag(string tagName)
{
Tag tag = context.Tags.Where(t => t.Name == tagName).Include("Recipes").FirstOrDefault();
if (tag == null)
{
tag = new Tag
{
Name = tagName,
Recipes = new List<Recipe>()
};
context.Tags.Add(tag);
}
return tag;
}
RecipeRepo - GetTagList
private IList<String> GetTagList(string tagString)
{
IList<string> tagList = new List<string>(Regex.Split(tagString, #"\,\s*"));
return tagList;
}
RecipeRepo - AssignTags
public void AssignTags(Recipe recipe, string tagString)
{
if (recipe.Tags == null)
recipe.Tags = new List<Tag>();
IList<string> tags = GetTagList(tagString);
foreach (string tagName in tags)
{
Tag tag = tagRepository.FindOrCreateTag(tagName);
if (tag.Recipes == null)
tag.Recipes = new List<Recipe>();
if (!tag.Recipes.Any(r => r.RecipeId == recipe.RecipeId))
tag.Recipes.Add(recipe);
if (recipe.Tags.All(t => t.TagId != tag.TagId))
recipe.Tags.Add(tag);
}
}
And in the end, Im calling this.
RecipeRepo - Update
public bool Update(Recipe recipe)
{
if (recipe.TagList != null)
AssignTags(recipe, recipe.TagList);
Recipe dbEnt = context.Recipes.Find(recipe.RecipeId);
context.Entry(dbEnt).CurrentValues.SetValues(recipe);
return Save();
}
What happens is - It takes the String, splits it up correctly - but after that, things seem to go south a bit. Instead of simply assigning the new tag, it duplicates the Recipe Object.
Whats is it, that Im missing?
Object that is retrieved from the context (such as a result of Find) should be 'Attached' before it has changes tracking capabilities, and is possible to be updated in the DB.
You should also mark it as 'Modified'.
For example, a proper update method should be something like:
public bool Update(Recipe recipe)
{
if (recipe.TagList != null)
AssignTags(recipe, recipe.TagList);
Recipe dbEnt = context.Recipes.Find(recipe.RecipeId);
if (context.Entry(dbEnt).State == EntityState.Detached)
{
context.Set<Recipe>().Attach(dbEnt);
}
context.Entry(dbEnt).State = EntityState.Modified;
context.Entry(dbEnt).CurrentValues.SetValues(recipe);
return Save();
}
If you would like to make a generic update function, it should look as follows:
public void Update(TEntity entityToUpdate)
{
if (context.Entry(entityToUpdate).State == EntityState.Detached)
{
context.Set<TEntity>().Attach(entityToUpdate);
}
context.Entry(entityToUpdate).State = EntityState.Modified;
}

Categories