View is not showing data after request - c#

I have an entity framework model as:
public class Campaign
{
public string Name { get; set; } = string.Empty;
public ICollection<CampaignStation> Stations { get; set; } = new List<CampaignStation>();
public ICollection<Order> Orders { get; set; } = new List<Order>();
}
public void Configure(EntityTypeBuilder<Campaign> builder)
{
builder.HasMany(x => x.Stations).WithOne(y => y.Campaign);
builder.HasMany(x => x.Orders).WithOne(y => y.Campaign);
}
So I have a get controller to get results, and I get results as shown in the following picture:
Apparently the service is working correctly
Now in the view, the status shows an error:
But the header show status OK
I have no idea what is wrong. If I try to access a model property like
<p>#Model.Select(x=> x.Name).ToString()</p>
It shows:
System.Linq.Enumerable+SelectListIterator`2[Project.Lib.Models.Campaign,System.String]
Controller:
[HttpGet]
public async Task<IActionResult> Index()
{
var results = await _campaignsService.GetCampaignsAsync();
return View(results);
}
Service:
public async Task<IList<Campaign>> GetCampaignsAsync()
{
return await _db.Campaigns
.Include(a => a.Agency)
.Include(s => s.Stations)
.ThenInclude(cs => cs.Station)
.ToListAsync();
}
UPDATE
Apparently is something with the EF Migration, I removed the collections and created a new migration and now it does not throw any error, it shows the Name property, etc. But now the question is, how can I return the list of my other two tables and not make any conflict? for some reason public ICollection<CampaignStation> Stations { get; set; } = new List<CampaignStation>(); it is not working but as in my image, it is returning the list correctly!

Related

context is null after adding new column/property

I've been modifying my existing project in mvc-c# and add a new column in one my tables.
Now, I modified my model and add this column polads
[Table("media_order_headers")]
public class OrderHeader
{
[Key]
public int id { get; set; }
/*list of other columns here */
public byte active { get; set; }
public byte with_package { get; set; }
public byte polads { get; set; } //newly added column
So now, i have added this to my Add and Edit controllers
[HttpPost]
public async Task<IActionResult> Add([FromBody]OrderHeaderViewModel model)
{
OrderHeader ord = new OrderHeader
{
/*list of other property value assigning*/
confirmed = model.confirmed,
with_package = model.with_package,
polads = model.polads
};
_context.OrderHeader.Add(ord);
await _context.SaveChangesAsync();
[HttpPost]
public async Task<IActionResult> Edit([FromBody]OrderHeaderViewModel model)
{
var ord = await _context.OrderHeader.Where(f => f.id == model.id).FirstOrDefaultAsync<OrderHeader>();
if (ord != null)
{
ord.with_package = model.with_package;
ord.billing_guide_no = model.bgnumber;
ord.polads = model.polads;
await _context.SaveChangesAsync();
I have not encountered a problem on Add function but in Edit Function.
It doesnt show exception error but it says on the console is internal server error 500. My variable ord is null. When I _context for internal exceptions it shows object not set to an instance of an object.
I have put a breakpoint on this line of script:
var ord = await _context.OrderHeader.Where(f => f.id == model.id).FirstOrDefaultAsync<OrderHeader>();
The view is returning set of values from the form.
my question is that, what might be the reason for this null error if it works with Add controller?
Any idea will be appreciated.
Thanks !

Using a nuget package produces Core SqlClient Data Provider invalid column error but tests within package are fine

[Update: I am able to reproduce in tests by using a dev database - a real SQL db]
I have a project that gets packaged up for use in other projects. The entities in question are:
public class PlatformQuizConfigurationEntity
{
public int Id { get; set; }
...
}
public class PlatformEnrollmentEntity
{
public int Id { get; set; }
public int PlatformQuizConfigurationId { get; set; }
public int? CourseSectionId { get; set; }
[ForeignKey("PlatformQuizConfigurationId")]
public PlatformQuizConfigurationEntity PlatformQuizConfiguration { get; set; }
...
public int PlatformEnrollmentStatusId { get; set; }
[ForeignKey("PlatformEnrollmentStatusId")]
public PlatformEnrollmentStatusEntity PlatformEnrollmentStatus { get; set; }
public DateTime CreatedDate { get; set; }
public int PlatformEnrollmentDeliveryId { get; set; }
[ForeignKey("PlatformEnrollmentDeliveryId")]
public PlatformEnrollmentDelivery PlatformEnrollmentDeliveryEntity { get; set; }
}
Within the DbContext, I have
public virtual DbSet<PlatformEnrollmentEntity> PlatformEnrollments { get; set; }
...
public virtual DbSet<PlatformQuizConfigurationEntity> PlatformQuizConfigurations { get; set; }
...
I have test coverage for this which uses in memory sqlite. I seed PlatformEnrollments with:
PlatformQuizConfigurationEntity pqc = _db.PlatformQuizConfigurations
.SingleOrDefault(s => s.ContinuingEducationData == "dummy-data");
LtiUserEntity lti = _db.LtiUsers.SingleOrDefault(s => s.PlatformUserId == this.defaultUser.PlatformUserId);
_db.PlatformEnrollments.Add(
new PlatformEnrollmentEntity
{
PlatformQuizConfigurationId = pqc.Id,
CourseSectionId = 401,
UserId = lti.Id,
PlatformEnrollmentStatus = new PlatformEnrollmentStatusEntity
{
IsActive = true,
IsActiveUpdated = DateTime.UtcNow,
IsComplete = false,
ReceivedLastChance = false
},
PlatformEnrollmentDelivery = new PlatformEnrollmentDeliveryEntity
{
Mode = "email",
ModeTarget = "test#example.org",
Hour = 15,
Minute = 00
}
}
);
_db.SaveChanges();
defaultPlatformEnrollment = _db.PlatformEnrollments.SingleOrDefault(p => p.UserId == lti.Id && p.CourseSectionId == 401);
And then a simple test
public IPlatformEnrollmentRepository repo = null;
public IPlatformEnrollmentService service = null;
public PlatformEnrollmentsTests()
{
repo = new PlatformEnrollmentRepository(_db, mapper, loggerFactory);
service = new PlatformEnrollmentService(repo);
}
...
[Fact]
public async Task PlatformEnrollment_Get()
{
var existing = await service.GetPlatformEnrollment(defaultPlatformEnrollment.Id);
Assert.IsType<PlatformEnrollment>(existing);
Assert.Equal("dummy-data", existing.PlatformQuizConfiguration.ContinuingEducationData);
}
The test calls the service
public async Task<PlatformEnrollment> GetPlatformEnrollment(int id)
{
return await _platformRepository.GetPlatformEnrollment(id);
}
which calls the repository and returns the Platform enrollment
var result = await _db.PlatformEnrollments
.AsNoTracking()
.Include(p => p.PlatformEnrollmentDelivery)
.Include(p => p.PlatformEnrollmentStatus)
.Include(p => p.PlatformQuizConfiguration)
.Include(p => p.PlatformQuizConfiguration.QuizType)
.Include(p => p.PlatformQuizConfiguration.ScheduledQuestions)
.SingleOrDefaultAsync(p => p.Id == id);
return _mapper.Map<PlatformEnrollmentEntity, PlatformEnrollment>(result);
The test passes. However, when I package this and install in another project (Azure Functions project, HTTP Trigger), then call it like:
var result = await _platformEnrollmentService.GetPlatformEnrollment(id);
if (result == null)
{
return new StatusCodeResult(StatusCodes.Status404NotFound);
}
return new OkObjectResult(result);
I get a 500 error:
Executed 'Get_PlatformEnrollment' ()
System.Private.CoreLib: Exception while executing function: Get_PlatformEnrollment. Core Microsoft SqlClient Data Provider: Invalid column name 'PlatformQuizConfigurationEntityId'.
Invalid column name 'PlatformQuizConfigurationEntityId'.
No table in the database has a PlatformQuizConfigurationEntityId column, only PlatformQuizConfigurationId so I'm assuming I just have something in my models/db context misconfigured.
I'm not sure if I need configuration in the project that uses the package, or if I'm missing something in the package.
If I'm missing something in the package, I'm confused why the test is passing.
If I'm missing something in the project that uses the package, that might confuse me even more since the project really doesn't care what the entities look like - it's just passing along relevant status codes and data.
As expected, I messed up a configuration. In order to reproduce, I updated the test suite to use a real database: our dev instance...
var options2 = new DbContextOptionsBuilder<OurDbContext>()
.UseSqlServer("your database connection string here")
.Options;
_db = new OurDbContext(options2);
Now that I am using this database, I ran the test with a known id:
var existing = await service.GetPlatformEnrollment(363);
Assert.IsType<PlatformEnrollment>(existing);
Assert.Equal("dummy-data", existing.PlatformQuizConfiguration.ContinuingEducationData);
This resulted in the same error I got in testing the project that uses this package. I commented out all of the includes in the method which resulted in the test not getting the exception. One by one I added them in until, of course, the last one caused the exception to be thrown.
public async Task<PlatformEnrollment> GetPlatformEnrollment(int id)
{
var result = await _db.PlatformEnrollments
.AsNoTracking()
.Include(p => p.PlatformEnrollmentDelivery)
.Include(p => p.PlatformEnrollmentStatus)
.Include(p => p.PlatformQuizConfiguration)
.Include(p => p.PlatformQuizConfiguration.QuizType)
.Include(p => p.PlatformQuizConfiguration.ScheduledQuestions)
.SingleOrDefaultAsync(p => p.Id == id);
return _mapper.Map<PlatformEnrollmentEntity, PlatformEnrollment>(result);
}
I was missing the Foreign Key Attribute on the property within ScheduledQuestionEntity:
[ForeignKey("PlatformQuizConfigurationId")]
public PlatformQuizConfigurationEntity PlatformQuizConfiguration { get; set; }
I have come across some information that leads me to believe foreign keys in sqlite aren't very dependable, which is why the tests were passing in this isolated environment.

AutoMapper cannot resolve Object from JSON string

So I am having an issue using AutoMapper to resolve my Cart items from the database where they are stored as a string. I am using a passing a CustomerCartDto to the front-end. When I manually map the properties everything works but I can't seem to get AutoMapper to recognize how to map the the items.
This is what I have in the controller. I have commented out the manual mapping below that works. The mapper function above that code block is what throws the error.
public async Task<ActionResult<CustomerCartDto>> GetCurrentUserCart()
{
var user = await _userManager.FindUserByClaimsPrinciple(HttpContext.User);
var cart = await _unitOfWork.Carts.GetCartAsync(user);
await _unitOfWork.Complete();
var returnedCart = _mapper.Map<CustomerCart, CustomerCartDto>(cart);
// var returnedCart = new CustomerCartDto
// {
// Id = cart.Id,
// Username = cart.AppUser.UserName,
// Email = cart.AppUser.Email,
// Items = JsonConvert.DeserializeObject<List<CartItemDto>>(cart.Items)
//};
return Ok(returnedCart);
}
I have the mapping profiles pulled out into another file here:
CreateMap<CustomerCart, CustomerCartDto>()
.ForMember(d => d.Items, o => o.MapFrom<CartItemsFromJsonResolver>())
.ForMember(d => d.Username, o => o.MapFrom(d => d.AppUser.UserName))
.ForMember(d => d.Username, o => o.MapFrom(d => d.AppUser.Email));
Because I am mapping from a JSON string to a class I have the actually resolver in another function here:
public class CartItemsFromJsonResolver: IValueResolver<CustomerCart, CustomerCartDto, List<CartItemDto>>
{
public CartItemsFromJsonResolver()
{
//
}
public List<CartItemDto> Resolve(CustomerCart source, CustomerCartDto destination, List<CartItemDto> destMember, ResolutionContext context)
{
if (!string.IsNullOrEmpty(source.Items))
{
return JsonConvert.DeserializeObject<List<CartItemDto>>(source.Items);
}
return null;
}
}
As I said if I manually map the properties in my controller and don't use AutoMapper it works fine without any issues but I would like to keep my controller as skinny as possible. Here is the error that gets thrown out in Postman.
Image of Postman Error
Edit : Here are the class being used:
public class CustomerCartDto
{
public CustomerCartDto(AppUser appUser, List<CartItemDto> items)
{
Items = items;
Username = appUser.UserName;
}
public CustomerCartDto(AppUser appUser, List<CartItemDto> items, string id)
{
Id = id;
Items = items;
Username = appUser.UserName;
}
public CustomerCartDto()
{
//
}
public string Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public List<CartItemDto> Items { get; set; }
}
And also how the model stored in the DB -
public class CustomerCart
{
public string Id { get; set; }
public AppUser AppUser { get; set; }
public string Items { get; set; }
// public float Subtotal { get; set; }
}
I don't know how IValueResolver is supposed to work in AutoMapper mapping configuration, but since your manual mapping works just fine, I'm assuming this line -
Items = JsonConvert.DeserializeObject<List<CartItemDto>>(cart.Items)
in your manual mapping is doing it's job as expected.
If so, then changing your current mapping configuration to following should solve your issue -
CreateMap<CustomerCart, CustomerCartDto>()
.ForMember(d => d.Username, o => o.MapFrom(s => s.AppUser.UserName))
.ForMember(d => d.Username, o => o.MapFrom(s => s.AppUser.Email))
.AfterMap((s, d) =>
{
d.Items = JsonConverter.DeserializeObject<List<CartItemDto>>(s.Items);
});
Let us know if your issue still remains.
Edit :
And also, rename Items property to something else in either CustomerCart or CustomerCartDto class. That's because when a map is not defined for a property, AutoMapper will look for a identically named property in the source model and automatically map from it.

Why RowVersion property is not raising optimistic concurrency exception when two concurrent changes occur?

I have the following entity,
public class PatientRegistry : BaseEntity {
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Patient File Number")]
public long PatientFileId { get; set; }
public virtual ICollection<PartnerRegistry> Partners { get; set; }
public string UserId { get; set; }
public string AliasName { get; set; }
public DateTime? DateNow { get; set; }
public virtual ApplicationUser User { get; set; }
}
and the BaseEntity is,
public class BaseEntity : ISoftDelete {
public bool IsDeleted { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
the entity has RowVersion property and I use the fluent API to configure it as RowVersion. I use the repository pattern and this is how I load the entity in the context,
public async Task<PatientRegistry> GetPatient(long Id, bool isPartners = true, bool isUser = true) {
if (isPartners && !isUser)
return await context.PatientsRegistry
.IgnoreQueryFilters()
.Include(pt => pt.Partners)
.Include(pt => pt.User)
.Include(pt => pt.User.Gender)
.Include(pt => pt.User.Nationality)
.Include(pt => pt.User.Occupation)
.Include(pt => pt.User.Ethnicity)
.Include(pt => pt.User.MaritalStatus)
.Include(pt => pt.User.Country)
.Include(pt => pt.User.State)
.Include(pt => pt.User.City)
.Include(pt => pt.User.Database)
.Include(pt => pt.User.UserAvatar)
.SingleOrDefaultAsync(pt => pt.PatientFileId == Id);
}
and in my controller, I update as following,
[HttpPut("{FileId}")]
public async Task<IActionResult> UpdatePatient([FromRoute] PatientFileIdResource fileIdModel, [FromBody] SavePatientsRegistryResource model)
{
ApiSuccessResponder<PatientRegistryResource> response = new ApiSuccessResponder<PatientRegistryResource>();
if (!ModelState.IsValid)
return BadRequest(ModelState);
Task<PatientRegistry> getPatientTask = repository.GetPatient(fileIdModel.FileId.Value);
Task<RequestHeaderResource> getHeaderRequestTask = headerRequestService.GetUserHeaderData();
await Task.WhenAll(getPatientTask, getHeaderRequestTask);
PatientRegistry patient = await getPatientTask.ConfigureAwait(false);
RequestHeaderResource header = await getHeaderRequestTask.ConfigureAwait(false);
if (patient == null)
{
ModelState.AddModelError(string.Empty, "The patient does not exist in the database");
return BadRequest(ModelState);
}
patient.DateNow = DateTime.Now;
patient.AliasName = model.AliasName;
await unitOfWork.CompleteAsync(header);
return StatusCode(200, response);
}
Now if I access the same record with two different users and one of the alters AliasName and saves the second user that accessed the record will still be able to save new changes without I get a concurrency exception. why are the concurrent updates here not raising an update exception?
this is the Fluent API config,
builder.Entity<PatientRegistry>()
.Property(a => a.RowVersion).IsRowVersion()
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate();
I have the update log here
UPDATE
For future reference,
Based on the docs here, we can test for concurrency conflict by just manipulating one of the values before calling save.
i.e.
context.Database.ExecuteSqlCommand("UPDATE dbo.PatientsRegistry SET AliasName = 'Jane' WHERE PatientFileId = 2222");
await context.SaveChangesAsync();
this will raise up an exception.
If the read from second user is after the write from first user then the second user's RowVersion will reflect the value at the time (i.e. after the first update). Thus - no concurrency issue.
You will only get an issue if they both read (and thus get the same RowVersion) and then both try and write.

Controller returns a list of data instead of single item, how to solve this issue?

Background
Recently I changed jobs and attached to a Web API project. I am familiar with the concepts of Web API & MVC but have no prior hands-on experince.
I have followed few tutorials and based on them created an empty WebApi project via Visual Studio 2017, hooked up my model from Database and added Controllers.
This is the revised controller:
private MyEntities db = new MyEntities();
//...
[ResponseType(typeof(MyEntityType))]
[Route("api/MyEntity")]
public async Task<IHttpActionResult> GetMyEntityType([FromUri]int parameter)
{
MyEntityType found = db.MyEntity
.OrderByDescending(c => c.CreationTime)
.First(c => c.ParameterColumn == parameter);
if (found == null)
{
return NotFound();
}
return Ok(found );
}
Note : I am querying based on a column other than KEY
When I make a call to .../api/MyEntity?parameter=1 I expect to receive a single item in response. But for reasons unknown to me, the previous call returns all items and it is unsorted.
Please note: If I place a breakpoint on if (found == null), I can confirm that my query has resulted in a single item.
Question
What am I missing here? Why does the response contains all elements instead of single element?
UPDATE 1
I tried the same call from Postman, this is the output. Please note that I have changed the request, controller code etc. in question to omit some private details.
I can see that response contains my desired data, but along with all of inner data in other end of relationship. If I am not mistaken, by default, EF uses lazy loading. Since I have no Include clause, I have no idea why all related data is returned in response.
I think I need to investigate my relationships in Model/DB and make sure Lazy-Loading is enabled.
UPDATE 2
These are my entity classes:
public partial class MyEntity
{
public int Id { get; set; }
public Nullable<int> ForeignKey_ID { get; set; }
public Nullable<int> MyValue { get; set; }
public Nullable<System.DateTime> CreationTime { get; set; }
public Nullable<int> Some_ID { get; set; }
public virtual MyOtherEntity MyOtherEntity { get; set; }
}
public partial class MyOtherEntity
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MyOtherEntity()
{
this.MyOtherEntity1 = new HashSet<MyOtherEntity>();
}
//...
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MyOtherEntity> MyOtherEntity1 { get; set; }
public virtual MyOtherEntity MyOtherEntity2 { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MyEntity> MyEntity { get; set; }
}
When the MyEntityType instance is returned via Ok it will be converted to JSON which will read the values of all the public properties and fields. This will cause EF to load the entire entity and all relationships. If you only require specific properties to be returned then use Select() as below.
var found = db.MyEntity
.OrderByDescending(c => c.CreationTime)
.Select(c => new { c.CreationTime, c.ParameterColumn })
.First(c => c.ParameterColumn == parameter);
You can tailor the selected properties as required. If you require criteria in First() that does not need to be selected, move the condition into a Where() call before the Select.
var found = db.MyEntity
.OrderByDescending(c => c.CreationTime)
.Where(c => c.ParameterColumn == parameter)
.Select(c => new { c.CreationTime })
.First();
You should probably create a ViewModel of MyEntityType that has only the properties you require, and map these to a new instance of the ViewModel instead. You can then update the [ResponseType(typeof(MyEntityType))] attribute too.
For example, declare:
public class MyEntityTypeViewModel {
public DateTime CreationTime { get; set; }
public int ParameterColumn { get; set; }
}
And then in your controller action:
MyEntityTypeViewModel found = db.MyEntity
.OrderByDescending(c => c.CreationTime)
.Where(c => c.ParameterColumn == parameter)
.Select(c => new MyEntityTypeViewModel {
CreationTime = c.CreationTime,
ParameterColumn = c.ParameterColumn })
.First();
If you're using Entity Framework Core 2, try this:
MyEntityType found = await db.MyEntity.AsNoTracking()
.OrderByDescending(c => c.CreationTime)
.FirstOrDefaultAsync(c => c.ParameterColumn == parameter);
And it's better to include database context via Dependency Injection, not with private field.

Categories