I have a piece of code that should retrieve a List of User objects.
public List<User> GetUsersBySessions(string[] sessionStrs, string ip)
{
if (sessionStrs == null || string.IsNullOrEmpty(ip))
return new List<User>();
using (var ctx = new DataContext())
{
var ret = ctx.Sessions.Include("User").Where(s => sessionStrs.Contains(s.ID) && s.IP.Equals(ip)).Select(s => s.User).ToList();
return ret;
}
}
The arguments sessionStrs and ip are properly passed into the method. However, I'm getting the following error:
(source: imgbomb.com)
How could this type of error be caused when I'm not using any .First() or .Single()? I'm just trying to get all items in a table that fit into an array return them as a List. I've never seen such a problem before.
The following line of code even causes an error:
var ret = ctx.Sessions.ToList();
Here is my DataContext:
public class DataContext : DbContext
{
public GenericDataContext()
: base(CONNECTION_STRING) //My CONNECTION_STRING is defined somewhere else, but I'm going to hide that for security's sake.
{
}
public DbSet<Password> Passwords { get; set; }
public DbSet<Session> Sessions { get; set; }
public DbSet<User> Users { get; set; }
}
And here is my Session model:
[Table("tbl_Sessions")]
public class Session
{
[Column("SessionID")]
[MaxLength(24)]
[Required]
[Key]
public string ID { get; set; }
[Column("UserID")]
[Required]
public int UserID { get; set; }
[Column("IP")]
[MaxLength(24)]
[Required]
public string IP { get; set; }
[ForeignKey("UserID")]
public virtual User User { get; set; }
}
NOTE
Both Classes are properly namespaced and have their proper using statements, as well.
The actual answer to this question had to do with the fact that one of my models had two Keys, each of which was a string. When I changed said model to have an int as it's Key, all worked well.
May be sessionStrs is null:
public List<User> GetUsersBySessions(string[] sessionStrs, string ip)
{
using (var ctx = new DataContext())
{
var ret = ctx.Sessions.Include("User");
if(sessionStrs!=null && sessionStrs.Any())
ret =ret.Where(s => sessionStrs.Contains(s.ID));
if(!string.IsNullOrEmpty(ip))
ret =ret.Where(s => s.IP.Equals(ip));
return ret.Any()?ret.Select(s => s.User).ToList():NULL;
}
}
Try to check if you have elements before selecting:
var ret = ctx.Sessions.Where(s => sessionStrs.Contains(s.ID) && s.IP.Equals(ip));
if (ret.Any())
return ret.Select(s => s.User).ToList();
else
return null; // or new List<User>();
Google led me here, so I'll throw my experience out there for anyone having a similar issue.
My project was using EF fluent configuration to query an existing database. I couldn't figure out why I was receiving any data back despite being able to query the table with other tools.
After banging my head against the wall, I finally discovered that I'd mislabeled the column type like so:
Property(x => x.DateCreated)
.HasColumnName("datetiem") // strings are the devil
Make sure you're using FirstOrDefault instead of First if you're extracting something from a list.
Related
TLDR - The error is:
The query has been configured to use 'QuerySplittingBehavior.SplitQuery' and contains a collection in the 'Select' call, which could not be split into separate query. Please remove 'AsSplitQuery' if applied or add 'AsSingleQuery' to the query.
I am developing a backend with EntityFrameworkCore in C#.
My table classes are like this:
public class MainTable : BasicAggregateRoot<int>
{
public MainTable()
{
this.Operations = new HashSet<OperationTable>();
}
public long? RecId { get; set; }
public int FormStatus { get; set; }
public virtual ICollection<OperationTable> Operations { get; set; }
}
public class OperationTable : BasicAggregateRoot<int>
{
public OperationTable()
{
this.Works = new HashSet<Work>(); //Not important things
this.Materials = new HashSet<Material>(); //Not important things
}
public string ServiceType { get; set; }
}
And my DTOs are like this:
public class MainDto : EntityDto<int>
{
public long? RecId { get; set; }
public int FormStatus { get; set; }
public List<OperationDto> Operations { get; set; }
}
public class OperationDto
{
public string ServiceType { get; set; }
}
I created maps this way:
CreateMap<MainTable, MainDto>().ReverseMap();
CreateMap<OperationTable, OperationDto>().ReverseMap();
When I commit the mapping by:
class Service{
IRepository<MainTable, int> _mainTableRepository;
Service(IRepository<MainTable, int> mainTableRepository){
_mainTableRepository = mainTableRepository;
}
List<MainDto> All()
{
var result = mainTableRepository.Include(p => p.Operations)
.ProjectTo<MainDto>(ObjectMapper.GetMapper().ConfigurationProvider) //Here is the problem.
.ToList();
return result;
}
}
I get the error on the top.
When I get rid of the List from mainDto, error does not occur, but I don't have the result that I want either.
What might be the problem? I couldn't find an answer.
In that source you can find the differences between single and split query: https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries
The problem is (I guess) IRepository.Include uses split query by default. But (again I guess) AutoMapper is not configured to use split query, it works with single queries.
We need to change the query type before mapping like this:
var result = mainTableRepository.Include(p => p.Operations)
.AsSingleQuery() //This solved the problem
.ProjectTo<MainDto>(ObjectMapper.GetMapper().ConfigurationProvider)
.ToList();
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
I've got a GET entry point on UsersController. But this one is responding an array of empty objects. When I debug the entry point, the list (IEnumerable<UserViewModel>) is not empty and UserViewModels are not empty either.
But here is what it does respond:
[{},{},{},{},{},{},{},{},{},{},{},{}]
And the controller code:
[HttpGet]
public async Task<IEnumerable<UserViewModel>> Get()
{
var u = _stObjMap.Default.Obtain<UserTable>();
using (var ctx = new SqlStandardCallContext())
{
var a = await ctx[u].Connection.QueryAsync<UserViewModel>("SELECT UserId, UserName, PrimaryEMail, CreationDate from CK.vUser where UserId <> 0 and UserId <> 1");
return a;
}
}
I really don't think that the problem is coming from the controller.
I'm using Dapper.I really don't know what is happening. And I have to write a lot because I can't edit this post
Most likely the UserViewModel has no public properties defined.
Which is why no key value pairs are displayed when the object is serialized to JSON.
Based on the query, make sure there are matching properties in the model for the Dapper query to map the selected columns
public class UserViewModel
public int UserId { get; set; }
public string UserName { get; set; }
public string PrimaryEMail { get; set; }
public DateTime CreationDate { get; set; }
}
This question already has answers here:
There is already an open DataReader associated with this Command which must be closed first
(19 answers)
Closed 6 years ago.
Pretty much I have 2 models - A announcement where some can post an announcement and a seen model which determins if someone has seen the announcement. her eis the models:
Announce:
public class Announcement
{
public int AnnouncementId { get; set; }
public string AnnouncementContent { get; set; }
public virtual ApplicationUser User { get; set; }
}
and Seen:
public class Seen
{
public int SeenId { get; set; }
public virtual Announcement Announcement { get; set; }
public virtual ApplicationUser User { get; set; }
}
in my AnnouncementController.Index I have this code which pretty much supposed to be, if you view this page, mark off every announcement as seen bbut am getting errors at the "new seen" part:
public ActionResult Index()
{
string currentUserId = User.Identity.GetUserId();
var currentUser = db.Users.FirstOrDefault(x => x.Id == currentUserId);
if(db.Announcements != null)
{
foreach (Announcement anoun in db.Announcements)
{
new Seen
{
User = db.Users.Add(currentUser),
Announcement = db.Announcements.FirstOrDefault(x => x.AnnouncementId == anoun.AnnouncementId),
};
}
}
return View(db.Announcements.ToList());
}
There is already an open DataReader associated with this Command which must be closed first.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public DbSet<Announcement> Announcements { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<Seen> Seens { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
Event if you did not have that error, you still have other issues. See below:
public ActionResult Index() {
string currentUserId = User.Identity.GetUserId();
var currentUser = db.Users.FirstOrDefault( x => x.Id == currentUserId );
List<Seen> seens = new List<Seen>();
if( db.Announcements != null ) {
foreach( Announcement anoun in db.Announcements ) {
seens.Add(
new Seen
{
User = currentUser, // You have this already so why go to the database again?
Announcement = anoun, // Same with this.
});
}
}
// Save seens to the database
return View( db.Announcements.ToList() );
The error is because you are actively iterating over a collection streaming from the database and then trying to re-issue another select to the database inside that loop. It can be fixed by forcing the materialization of the collection before you enter the loop using ToList() (other options are also available like AsEnumerable or ToArray).
foreach (Announcement anoun in db.Announcements.ToList())
{
new Seen
{
User = db.Users.Add(currentUser),
Announcement = db.Announcements.FirstOrDefault(x => x.AnnouncementId == anoun.AnnouncementId),
};
}
That being said I am not sure why you are doing it this way. Why not attach the instance anoun directly as you are only using a single (or same in the code shown) DbContext instance (variable named db).
foreach (Announcement anoun in db.Announcements)
{
new Seen
{
User = currentUser, // also this seemed wrong before, just assign the reference directly
Announcement = anoun
};
}
Or make it even more simple:
var newSeens = db.Announcements.Select(x => new Seen(){User = currentUser, Announcement = x}).ToList();
db.Seens.AddRange(newSeens);
db.SaveChanges();
This assumes that the user has not seen any announcement. If the user has seen some then you need to filter db.Announcements on existing Seen records for that user.
I am using windows azure table storage. My problem is that the access of entities for a given user from table is taking long time. The code which i am using to access the table is as follows :
public CloudTableQuery<T> GetEntites(string username)
{
try
{
CloudTableQuery<T> entries =
(from e in ServiceContext.CreateQuery<T>(TableName)
where e.PartitionKey == username
select e).AsTableServiceQuery();
return entries;
}
catch (Exception)
{ return null; }
}
The total entities in the table are currently only 100 or so. For example : The query seems to take as long as 40 seconds to return 25 entities for a given user. Please suggest if there is any scope for improvement in the code for faster performance ?
The real problem here is you are returning queries instead of entities, and you are likely executing a query again for each returned entity (query). Define your entity with something like:
public class UserEntity
{
public string UserName { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
Then define your table storage entity with something like:
public class StoredUserEntity : TableServiceEntity
{
public StoredUserEntity (string username)
{
this.PartitionKey = username;
this.RowKey = Guid.NewGuid().ToString();
this.Timestamp = DateTime.Now;
}
public string Email { get; set; }
public string Name { get; set; }
}
Then your query should actually return a list of UserEntities:
public List<UserEntity> GetUserData(string username)
{
var q = _tableContext.CreateQuery<StoredUserEntity>(_tableName);
return
q.ToList().Select(x => new UserEntity {UserName = x.PartitionKey, Email = x.Email, Name = x.Name}).Where(x => x.UserName == username).ToList();
}
Try using Fiddler to see what's going on. It could be that you are experiencing a few retries that could slow things down. The API won't tell you that, but with Fiddler you can see exactly the requests being made, any responses (along with errors) and how many of them are being made.