Model returns null when the site first loads - asp.net

So, I recently found quite an issue with my site: when it first loads, a section of the website is missing. After some tests, I found that this line was sometimes false: #if (Model != null && Model.Any()). After a test using a single Modal == null, I found that yes, the issue is that it's sometimes null. Also, I found that the best way for me to reproduce the issue (no error messages) is to restart visual studio. CTRL + F5 does not make it be null. Any ideas why is that ?
Here's the Model and the part of cshtml:
public class BlogModel
{
public int Id { get; set; }
public bool AfficheAuteur { get; set; }
public string Alias { get; set; }
public string Sujet { get; set; }
public string Auteur { get; set; }
public string Photo { get; set; }
public int? Ordre { get; set; }
public PostModel Post { get; set; }
}
public class PostModel
{
public int Id { get; set; }
public string Alias { get; set; }
public string Nom { get; set; }
}
//.cshtml:
#model IList<Project.Models.Shared.BlogModel>
//...
#if (Model != null && Model.Any())
//...
Note that I'm using asp.net Core MVC with razor.
Edit:
public static IList<BlogModel> GetBlogs()
{
var _lock = new object();
var strKey = string.Format("Home-Blogs-{0}", Site.Id);
var blogs = (IList<BlogModel>)CacheManager.Get(strKey);
if (blogs == null)
{
lock (_lock)
{
blogs = (IList<BlogModel>)CacheManager.Get(strKey);
if (blogs == null)
{
using (var context = new DB())
{
context.Configuration.LazyLoadingEnabled = false;
var nIdSite = Site.Id;
var bl = (from b in context.Blog
where b.Actif &&
(b.IdsSite.Contains("," + nIdSite + ",")) &&
b.Posts.Any(y => y.Publier)
orderby b.Ordre
select new BlogModel()
{
Id = b.Id,
AfficheAuteur = b.AfficherAuteur,
Alias = b.Alias,
Sujet = b.Sujet,
Photo = b.Image,
Auteur = b.User.Profile.FirstName + " " + b.User.Profile.LastName,
Ordre = b.Ordre,
Post = (from p in context.BlogPost
where p.Publier &&
p.IdBlog == b.Id &&
p.DateAffichage <= DateTime.Now
orderby p.DateAffichage descending
select new PostModel()
{
Id = p.Id,
Alias = p.Alias,
Nom = p.Nom
}).FirstOrDefault()
}).ToList();
CacheManager.Insert(strKey, bl, null, 10800, Cache.NoSlidingExpiration, CacheItemPriority.High, null);
return blogs;
}
}
}
}
return blogs;
}
public ActionResult Index(GridSettings settings, string strQuery)
{
var model = new IndexBlogViewModel(settings, blogService, strQuery);
ViewBag.FilAriane.Add(new KeyValuePair<string, string>(Url.Action("Index", "Home"), "Accueil"));
ViewBag.FilAriane.Add(new KeyValuePair<string, string>("", "Blogs"));
return View(model);
}
[HttpGet]
public ActionResult Create()
{
var model = new BlogFormViewModel { Blog = new Entitie.Blog { IdSite = IdSite } };
var lstUser = new List<User>();
var cfUserProvider = new CFUserProvider();
foreach (var mu in cfUserProvider.GetAllUsers().Cast<MembershipUser>())
{
var r = new CFRoleProvider();
if (r.IsUserInRole(mu.UserName, "Bloggeur"))
{
var u = new User { Username = mu.UserName, Id = Convert.ToInt32(mu.ProviderUserKey) };
lstUser.Add(u);
}
}
model.User = lstUser.Select(x => new SelectListItem
{
Text = x.Username,
Value = x.Id.ToString()
});
model.Sites = siteService.GetAll(x => x.IdEntreprise == IdEntreprise)
.Select(x => new CheckBoxListItem
{
Id = x.Id,
Display = x.Nom,
IsChecked = false
}).ToList();
ViewBag.FilAriane.Add(new KeyValuePair<string, string>(Url.Action("Index", "Home"), "Accueil"));
ViewBag.FilAriane.Add(new KeyValuePair<string, string>("", "Blog"));
return View(model);
}

Found it... It was checking for null and if it was, was adding it to cache but still returning the non-updated variable. Simply had to update it before returning...
Added:
blogs = (IList<BlogModel>)CacheManager.Get(strKey);
before returning.

Related

How to update entity desipite the row is loaded in EF change tracker or not in entity framework?

I allready look out everything of that problem but none of them worked for me.Everyone is suggestion to use AsNoTracking() for that problem but its has no sense with my problem because im not updating the data which i call from my database.
I have company profile update modal, this company can have profile photo or not but either way i need to update those informations. So that's why i need to control is comapny creating a photo or updating a photo. Let me show u to my code in the bellow :
#region /*UpdateCompanyProfile*/
[HttpPost]
public IActionResult UpdateCompanyProfile(Company company, List<IFormFile> files, int FileID)
{
try
{
if (ModelState.IsValid)
{
company.ModifiedDate = DateTime.Now;
_unitOfWorkC.RepositoryCompany.Update(company);
int firstRequest = HttpContext.Response.StatusCode;
if (firstRequest == 200)
{
_unitOfWorkC.Complete();
if (files.Count != 0)
{
var File = _fileUploader.FileUploadToDatabase(files);
var FileResult = File.Result;
FileResult.CompanyID = company.CompanyID;
if (FileID == 0)//That's the point where i control that file, is it gonna be update or create.
{
_unitOfWorkFR.RepositoryFileRepo.Create(FileResult);
int secondRequest1 = HttpContext.Response.StatusCode;
if (secondRequest1 == 200)
{
int tryCatch = _unitOfWorkFR.Complete();
if (tryCatch != 15)
{
TempData["JS"] = "showSuccess();";
}
else
{
TempData["JS"] = "showError();";
}
}
}
else
{
FileResult.FileID = FileID;
_unitOfWorkFR.RepositoryFileRepo.Update(FileResult); //That's the point where i get the error.
int secondRequest2 = HttpContext.Response.StatusCode;
if (secondRequest2 == 200)
{
int tryCatch2 = _unitOfWorkFR.Complete();
if (tryCatch2 != 15)
{
TempData["JS"] = "showSuccess();";
}
else
{
TempData["JS"] = "showError();";
}
}
else
{
TempData["JS"] = "showError();";
}
}
}
}
else
{
TempData["Message"] = "?irket g?ncelleme i?leminiz ba?ar?s?z!";
TempData["JS"] = "showError();";
return RedirectToAction("CompanyProfile");
}
}
else
{
TempData["Message"] = "G??ncellemek istedi?iniz veri hatal?!";
TempData["JS"] = "showError();";
return RedirectToAction("CompanyProfile");
}
}
catch (Exception ex)
{
var log = _logging.Logging(ex.Message, "Exception/Hata", company.CompanyID.ToString(),
"CompanyProfile/UpdateCompanyProfile", getCurrentUser(), getCurrentUserClaimRole());
_unitOfWorkLog.RepositoryLog.Create(log);
_unitOfWorkLog.Complete();
//TempData["Message"] = ex.Message;
//TempData["JS"] = "showError();";
return RedirectToAction("CompanyProfile");
}
}
#endregion
As u can see, calling that data with AsNoTracking() has no sense in my stuation. I'm only getting that error in that action,so other FileRepo actions are working well.
That's my FileUploadToDatabase() method :
public async Task<FileRepo> FileUploadToDatabase(List<IFormFile> files)
{
foreach (var file in files)
{
var fileName = Path.GetFileNameWithoutExtension(file.FileName);
var fileExtension = Path.GetExtension(file.FileName);
_fileRepo = new FileRepo
{
FileName = fileName,
FileExtension = fileExtension,
FileType = file.ContentType,
CreatedDate= DateTime.Now
};
using (var dataStream = new MemoryStream())
{
await file.CopyToAsync(dataStream);
_fileRepo.FileData = dataStream.ToArray();
}
}
return _fileRepo;
}
And that's my FileRepo class :
public class FileRepo : Base
{
[Key]
public int FileID { get; set; }
[Required(ErrorMessage = "Required Field !")]
public string FileName { get; set; }
[Required(ErrorMessage = "Required Field !")]
public string FileType { get; set; }
[Required(ErrorMessage = "Required Field !")]
public string FileExtension { get; set; }
public string FilePath { get; set; }
public bool FilePhotoIsDefault { get; set; }
public byte[] FileData { get; set; }
public int? CompanyID { get; set; }
public Company Company { get; set; }
#endregion
}
That's my UnitOfWork :
and This is my Repository :
This is the query for my Update Modal:
public IEnumerable<Company> GetByIDForCompanyProfileCompany(int ID)
{
return TradeTurkDBContext.Companies.Where(x => x.CompanyID == ID)
.Include(x => x.Addresses.Where(x => x.IsDeleted == null || x.IsDeleted == false))
//
.Include(x => x.Products.Where(x => x.IsDeleted == null || x.IsDeleted == false))
.ThenInclude(x => x.FileRepos.Where(x => x.IsDeleted == null || x.IsDeleted == false)).AsSplitQuery()
//
.AsNoTrackingWithIdentityResolution().ToList();
}
For updating FileResult you are using DbSet.Update - it is trying to attach entity to ChangeTracker. Attaching will fail if there is already attached object with the same key.
Change your repository to the following. It will update all fields if entity is not in ChangeTracker, otherwise it will correct only needed properties:
public void Update(T model)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
// I hope your generic repository knows Model Id property
var entry = _context.ChangeTracker.Entries<T>().FirstOrDefault(e => e.Entity.Id == model.Id);
if (entry == null)
{
// entity not tracked, so attach it
_dbSet.Update(model);
}
else
{
// setting values from not tracked object
if (!ReferenceEquals(model, entry.Entity))
entry.CurrentValues.SetValues(model);
}
}
UPDATE
If generic repository don't know about Id property, you can define interface for that:
public interface IEntityWithId
{
int Id {get;}
}
Make sure that your classes is implementation of IEntityWithId. Then correct Repository definition:
public interface IRepository<T> where T: class, IEntityWithId
{
...
}

FindOneAndUpdateAsync Intermittently Returning Null

I am using MongoDB.Driver for .NET Core 3.1 and running into an issue were records are not being saved properly. They are intermittently coming back as null when calling FindOneAndUpdateAsync. I have a script that calls my below code 100 times. Out of those 100, 1-5 fail in the last method, SetChildFavoritesAsync. The results came back as null. Any suggestions on what I am doing wrong?
Example Calls
var id = 1;
var childName = "test";
var collectionEntry = await FindByIdOrCreateAsync(id);
collectionEntry.Children = new List<MyCollection.ChildClass>{
new MyCollection.ChildClass{
Name = childName,
Favorites = new List<MyCollection.ChildClass.Favorite>()
}
};
await FindByIdAndUpdateChildrenAsync(collectionEntry.Id, collectionEntry.Children);
var favorites = new List<MyCollection.ChildClass.Favorite>{
Name = "testFavorite"
};
var resultsOfSet = await SetChildFavoritesAsync(id, name, favorites)
//do stuff with resultsOfSet
Example Model
public class MyCollection
{
[MongoDB.Bson.Serialization.Attributes.BsonRepresentation(BsonType.ObjectId)]
[MongoDB.Bson.Serialization.Attributes.BsonId]
public string _Id { get; set; }
[MongoDB.Bson.Serialization.Attributes.BsonRequired]
public int Id { get; set; }
public List<ChildClass> Children { get; set; }
public class ChildClass
{
public string Name { get; set; }
public List<Favorite> Favorites { get; set; }
public class Favorite
{
public string Name { get; set; }
}
}
}
Example Methods
public async Task<MyCollection> FindByIdOrCreateAsync(int id)
{
var filter = Builders<MyCollection>.Filter.Eq(mc => mc.Id, id);
var update = Builders<MyCollection>.Update
.Set(mc => mc.Id, id)
.SetOnInsert(mc => mc.Children, new List<MyCollection.ChildClass>());
var options = new FindOneAndUpdateOptions<MyCollection> { ReturnDocument = ReturnDocument.After, IsUpsert = true };
return await _database.GetCollection<MyCollection>("MyCollectionName").FindOneAndUpdateAsync(filter, update, options);
}
public async Task<MyCollection> FindByIdAndUpdateChildrenAsync(int collectionId, List<MyCollection.ChildClass> children)
{
var filter = Builders<MyCollection>.Filter.Eq(mc => mc.Id, collectionId);
var update = Builders<MyCollection>.Update.Set(mc => mc.Children, children);
var options = new FindOneAndUpdateOptions<MyCollection> { ReturnDocument = ReturnDocument.After, IsUpsert = false };
return await _database.GetCollection<MyCollection>("MyCollectionName").FindOneAndUpdateAsync(filter, update, options);
}
public async Task<MyCollection> SetChildFavoritesAsync(int collectionId, string childName, List<MyCollection.ChildClass.Favorite> favorites)
{
var filter = Builders<MyCollection>.Filter.Eq(mc => mc.Id, collectionId);
filter &= Builders<MyCollection>.Filter.Eq("children.name", childName);
var update = Builders<MyCollection>.Update.Set("children.$.favorites", favorites);
var options = new FindOneAndUpdateOptions<MyCollection> { ReturnDocument = ReturnDocument.After };
var results = await _database.GetCollection<MyCollection>("MyCollectionName").FindOneAndUpdateAsync(filter, update, options);
if (results == null)
{
_log.Error($"Child Favorites didn't save: collectionId:{collectionId}, childName:{childName}");
}
else
{
_log.Debug($"Child Favorites: collectionId:{collectionId}, childName:{childName}, favorites:{Newtonsoft.Json.JsonConvert.SerializeObject(results)}");
}
return results;
}
Appears to be an issue with communication to the database. I added some retry logic, which solved the issue.

ASP.Net MVC : Binding Dropdownlist to a List on the Model [duplicate]

I'm developing an ASP.NET MVC 5 application, with C# and .NET Framework 4.6.1.
I have this View:
#model MyProject.Web.API.Models.AggregationLevelConfViewModel
[...]
#Html.DropDownListFor(m => m.Configurations[0].HelperCodeType, (SelectList)Model.HelperCodeTypeItems, new { id = "Configurations[0].HelperCodeType" })
The ViewModel is:
public class AggregationLevelConfViewModel
{
private readonly List<GenericIdNameType> codeTypes;
private readonly List<GenericIdNameType> helperCodeTypes;
public IEnumerable<SelectListItem> CodeTypeItems
{
get { return new SelectList(codeTypes, "Id", "Name"); }
}
public IEnumerable<SelectListItem> HelperCodeTypeItems
{
get { return new SelectList(helperCodeTypes, "Id", "Name"); }
}
public int ProductionOrderId { get; set; }
public string ProductionOrderName { get; set; }
public IList<Models.AggregationLevelConfiguration> Configurations { get; set; }
public AggregationLevelConfViewModel()
{
// Load CodeTypes to show it as a DropDownList
byte[] values = (byte[])Enum.GetValues(typeof(CodeTypes));
codeTypes = new List<GenericIdNameType>();
helperCodeTypes = new List<GenericIdNameType>();
for (int i = 0; i < values.Length; i++)
{
GenericIdNameType cType = new GenericIdNameType()
{
Id = values[i].ToString(),
Name = EnumHelper.GetDescription((CodeTypes)values[i])
};
if (((CodeTypes)values[i]) != CodeTypes.NotUsed)
codeTypes.Add(cType);
helperCodeTypes.Add(cType);
}
}
}
And Models.AggregationLevelConfiguration is:
public class AggregationLevelConfiguration
{
public byte AggregationLevelConfigurationId { get; set; }
public int ProductionOrderId { get; set; }
public string Name { get; set; }
public byte CodeType { get; set; }
public byte HelperCodeType { get; set; }
public int PkgRatio { get; set; }
public int RemainingCodes { get; set; }
}
I need to set selected value in these properties:
public IEnumerable<SelectListItem> CodeTypeItems
{
get { return new SelectList(codeTypes, "Id", "Name"); }
}
public IEnumerable<SelectListItem> HelperCodeTypeItems
{
get { return new SelectList(helperCodeTypes, "Id", "Name"); }
}
But I can't set it in new SelectList(codeTypes, "Id", "Name"); or new SelectList(helperCodeTypes, "Id", "Name"); because the selected value are in Configurations array: fields AggregationLevelConfiguration.CodeType and AggregationLevelConfiguration.HelperCodeType.
I think I have to set selected value in the View, but I don't know how to do it.
How can I set the selected values?
Unfortunately #Html.DropDownListFor() behaves a little differently than other helpers when rendering controls in a loop. This has been previously reported as an issue on CodePlex (not sure if its a bug or just a limitation)
The are 2 option to solve this to ensure the correct option is selected based on the model property
Option 1 (using an EditorTemplate)
Create a custom EditorTemplate for the type in the collection. Create a partial in /Views/Shared/EditorTemplates/AggregationLevelConfiguration.cshtml (note the name must match the name of the type
#model yourAssembly.AggregationLevelConfiguration
#Html.DropDownListFor(m => m.HelperCodeType, (SelectList)ViewData["CodeTypeItems"])
.... // other properties of AggregationLevelConfiguration
and then in the main view, pass the SelectList to the EditorTemplate as additionalViewData
#using (Html.BeginForm())
{
...
#Html.EditorFor(m => m.Configurations , new { CodeTypeItems = Model.CodeTypeItems })
...
Option 2 (generate a new SelectList in each iteration and set the selectedValue)
In this option your property CodeTypeItems should to be IEnumerable<GenericIdNameType>, not a SelectList (or just make codeTypes a public property). Then in the main view
#Html.DropDownListFor(m => m.Configurations[0].HelperCodeType, new SelectList(Model.CodeTypeItems, "Id", "Name", Model.Configurations[0].HelperCodeType)
Side note: there is no need to use new { id = "Configurations[0].HelperCodeType" - the DropDownListFor() method already generated that id attribute
I wrote this class to overcome an issue I was having with selecting an option in an html select list. I hope it helps someone.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
namespace Login_page.Models
{
public class HTMLSelect
{
public string id { get; set; }
public IEnumerable<string> #class { get; set; }
public string name { get; set; }
public Boolean required { get; set; }
public string size { get; set; }
public IEnumerable<SelectOption> SelectOptions { get; set; }
public HTMLSelect(IEnumerable<SelectOption> options)
{
}
public HTMLSelect(string id, string name)
{
this.id = id;
this.name = name;
}
public HTMLSelect(string id, string name, bool required, IEnumerable<SelectOption> options)
{
this.id = id;
this.name = name;
this.required = required;
}
private string BuildOpeningTag()
{
StringBuilder text = new StringBuilder();
text.Append("<select");
text.Append(this.id != null ? " id=" + '"' + this.id + '"' : "");
text.Append(this.name != null ? " name=" + '"' + this.name + '"' : "");
text.Append(">");
return text.ToString();
}
public string GenerateSelect(IEnumerable<SelectOption> options)
{
StringBuilder selectElement = new StringBuilder();
selectElement.Append(this.BuildOpeningTag());
foreach (SelectOption option in options)
{
StringBuilder text = new StringBuilder();
text.Append("\t");
text.Append("<option value=" + '"' + option.Value + '"');
text.Append(option.Selected != false ? " selected=" + '"' + "selected" + '"' + ">" : ">");
text.Append(option.Text);
text.Append("</option>");
selectElement.Append(text.ToString());
}
selectElement.Append("</select");
return selectElement.ToString();
}
}
public class SelectOption
{
public string Text { get; set; }
public Boolean Selected { get; set; }
public string Value { get; set; }
}
}
And
public IEnumerable<SelectOption> getOrderTypes()
{
List<SelectOption> orderTypes = new List<SelectOption>();
if (this.orderType == "OptionText")
{
orderTypes.Add(new SelectOption() { Value = "1", Text = "OptionText", Selected = true });
} else
{
orderTypes.Add(new SelectOption() { Value = "2", Text = "OptionText2" });
}
}
And to use it:
#{
Login_page.Models.HTMLSelect selectElement = new Login_page.Models.HTMLSelect("order-types", "order-types");
}
#Html.Raw(selectElement.GenerateSelect(Model.getOrderTypes()));
I leave this in case it helps someone else. I had a very similar problem and none of the answers helped.
We had in a view this line at the top:
IEnumerable<SelectListItem> exitFromTrustDeed = (ViewData["ExitFromTrustDeed"] as IEnumerable<string>).Select(e => new SelectListItem() {
Value = e,
Text = e,
Selected = Model.ExitFromTrustDeed == e
});
and then below in the view:
#Html.DropDownListFor(m => m.ExitFromTrustDeed, exitFromTrustDeed, new { #class = "form-control" })
We had a property in my ViewData with the same name as the selector for the lambda expression and for some reason that makes the dropdown to be rendered without any option selected.
We changed the name in ViewData to ViewData["ExitFromTrustDeed2"] and that made it work as expected.
Weird though.

Web API & entitiy framework how to work with posted json array

I try to write a post method for my asp.net web API.
The method shoulde be
1. receive Json formated datastring with have single objects and one sub array.
2. write it in my database in two tables with have a 1:n relation.
It would be wonderfull if anyone can help me.
I've no more idea how i can realise it
Example of the Json data:
[
{
"User":"testuser",
"CPGRP":21321321,
"Sex":"men",
"Name": "test",
"PointList":[
{
"Cost_I_Rea":"22202771.01",
"Cost_TV":"213213210.0" ,
"GRP":10,
"ID":0,
"ReichweiteID_F":1,
"RW_TV":"9.603",
"RW_Zuwgs":"9.603",
},
{
"Cost_I_Rea":"22202771.01",
"Cost_TV":"213213210.0" ,
"GRP":10,
"ID":0,
"ReichweiteID_F":1,
"RW_TV":"9.61103",
"RW_Zuwgs":"9.6043",
}
]
"Potenzial":213213,
"ReichweiteID":0,
"ZielGRP":21321321
}
]
This is my Post Method now, but it's generate a 500 Error:
// POST api/TVKurve
[ResponseType(typeof(Points))]
public async Task<IHttpActionResult> PostPoints(Points points)
{
//if (!ModelState.IsValid)
//{
// return BadRequest(ModelState);
//}
db.Points.Add(points);
await db.SaveChangesAsync();
db.Entry(points).Reference(x => x.Reichweite).Load();
var dto = new ReichweitePointsDto()
{
ReichweiteID = points.ReichweiteId,
Sex = points.Reichweite.Geschlecht,
Name = points.Reichweite.Name,
CPGRP = points.Reichweite.CpGRP,
Potenzial = points.Reichweite.Potenzial,
ZielGRP = points.Reichweite.ZielGRP,
User = points.Reichweite.Benutzer,
PointList = new List<PointListDto>(),
};
return CreatedAtRoute("DefaultApi", new { id = points.Id }, points);
}
and my data annotation Model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace xms_ef_vers1.Models
{
public class PointListDto
{
public PointListDto() { }
public PointListDto(Points item)
{
ID = item.Id;
GRP = item.GRP;
RW_TV = item.RW_TV;
Cost_TV = item.Cost_TV;
RW_Zuwgs = item.Rw_ZuWGS;
Cost_I_Rea = item.Cost_I_Rea;
ReichweiteID_F = item.ReichweiteId;
}
[Key]
public int ID { get; set;}
[Required]
public int GRP { get; set; }
public decimal RW_TV { get; set; }
public double Cost_TV { get; set; }
public decimal RW_Zuwgs { get; set; }
public decimal Cost_I_Rea { get; set; }
public int ReichweiteID_F { get; set; }
public Points ToEntity()
{
return new Points
{
Id = ID,
GRP = GRP,
RW_TV = RW_TV,
Cost_TV = Cost_TV,
Rw_ZuWGS = RW_Zuwgs,
Cost_I_Rea = Cost_I_Rea,
ReichweiteId = ReichweiteID_F,
};
}
}
}
Thank you for your answer.
I ve got it.
Look here:
public HttpResponseMessage PostReicheite(ReichweitePointsDto reichw)
{
if (ModelState.IsValid)
{
var reichwe = new Reichweite()
{
Geschlecht = reichw.Geschlecht,
Name = reichw.Name,
CpGRP = reichw.CPGRP,
Potenzial = reichw.Potenzial,
ZielGRP = reichw.ZielGRP,
Benutzer = reichw.Benutzer,
PointListe = (from item in reichw.PointListe
select new Points()
{
GRP = item.GRP,
RW_TV = item.RW_TV,
Cost_TV = item.Cost_TV,
Rw_ZuWGS = item.RW_Zuwgs,
Cost_I_Rea = item.Cost_I_Rea,
}).ToList()
};
db.Reichweites.Add(reichwe);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, reichwe);
response.Headers.Location = new Uri(Url.Link("DefaultAPI", new { id = reichwe.Id }));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
Now the Post Method do exactly what i need.
Only one bug, i've got an 500 status back can you say me why?
Best

MVC3 Session State

I've recently been re-factoring my code to move from inproc to Session State. I've created a new class that I built from the start to be serializable. I'm currently getting the 'Unable to serialize the session state.' error. However, I am able to serialize the class using JsonConvert (newtonsoft).
Are methods not allowed or something?
This is what it looks like:
[Serializable()]
public class SessionStateObject
{
public int CurrentSessionId { get; set; }
public int CurrentAssessmentId { get; set; }
public int CurrentAssessmentElementId { get; set; }
public int CurrentUserId { get; set; }
public Dictionary<string, int> AssessmentItemsIds { get; set; }
public Dictionary<string, int> SessionResponseIds { get; set; }
public List<string> AdministeredItemOrderByName;
public string LastVisitedItem;
public int? TotalAssessmentCurrentItemOrder;
public RenderablePersistanceObject RenderablePersistance;
#region formula engine section variables
public string BranchingResult { get; set; }
public string ContentOutput { get; set; }
public int CurrentItemId { get; set; }
public int VirtualWorkingItemId { get; set; }
public bool isCloneItem { get; set; }
public bool wasPreFormulaRun;
public bool wasContentOutput;
public bool wasPrefillReached;
public bool isVirtual;
public List<string> FormulaStack { get; set; }
public List<string> ItemNameTokens { get; set; }
public string serializedJSContext { get; set; }
public string CloneSourceItem { get; set; }
public int itemsAbletoFastForwardThrough { get; set; }
#endregion
private Dictionary<int, int> itemIdByMultiGroupId; //key is itemId, val is groupId
private Dictionary<int, List<int>> MultiGroupIdByItemIds; //key is groupId, val is itemId
public SessionStateObject()
{
RenderablePersistance = new RenderablePersistanceObject();
AssessmentItemsIds = new Dictionary<string, int>();
SessionResponseIds = new Dictionary<string, int>();
AdministeredItemOrderByName = new List<string>();
FormulaStack = new List<string>();
ItemNameTokens = new List<string>();
itemIdByMultiGroupId = new Dictionary<int, int>();
MultiGroupIdByItemIds = new Dictionary<int, List<int>>();
}
public void initMultiItemGroups(Assessment assessment, NetScidDbContainer dbContext)
{
List<MultiItem> assessmentMultiItems = (from multi in dbContext.MultiItems
where multi.AssessmentId == assessment.Id
select multi).ToList();
List<int> uniqueGroupIds = new List<int>();
foreach (MultiItem mItem in assessmentMultiItems)
{
itemIdByMultiGroupId.Add(mItem.ItemId, mItem.GroupId);
if (!uniqueGroupIds.Contains(mItem.GroupId))
{
uniqueGroupIds.Add(mItem.GroupId);
}
}
foreach (int groupId in uniqueGroupIds)
{
List<int> ItemIds = (from itemGroup in assessmentMultiItems
where itemGroup.GroupId == groupId
orderby itemGroup.Id ascending
select itemGroup.ItemId).ToList();
MultiGroupIdByItemIds.Add(groupId, ItemIds);
}
}
public List<int> GetItemIdsFromSingleItemId(int itemId)
{
List<int> foundItemIDs = new List<int>();
int foundGroupId = -1;
if (this.itemIdByMultiGroupId.ContainsKey(itemId))
{
foundGroupId = this.itemIdByMultiGroupId[itemId];
}
if (this.MultiGroupIdByItemIds.ContainsKey(foundGroupId))
{
foundItemIDs = this.MultiGroupIdByItemIds[foundGroupId];
}
return foundItemIDs;
}
public void nullifyRenderable()
{
this.RenderablePersistance = null;
}
public void PersistRenderable(IRenderable renderable)
{
this.RenderablePersistance = new RenderablePersistanceObject();
if (renderable is MultiItemRenderable)
{
//get list of item IDs from the multi-item lookup
this.RenderablePersistance.isMultiItem = true;
this.RenderablePersistance.primaryItemId = ((Item)((MultiItemRenderable)renderable).IndexedItems.Last()).Id;
}
else //regular renderable
{
this.RenderablePersistance.isMultiItem = false;
this.RenderablePersistance.primaryItemId = ((Item)renderable).Id;
}
}
public AssessmentRuntime StartAdministrativeSession(NetScidDbContainer dataContext, Assessment assessment, User currentUser, string pid = "1")
{
AssessmentRuntime newRuntime = new AssessmentRuntime(this, dataContext);
Session newSession = new Session();
assessment.PrepElements();
PermissionEntity rootPE = new PermissionEntity();
if (currentUser != null)
{
rootPE = PermissionEntityHelper.GetRootPermissionEnity(dataContext, currentUser);
}
else
{
rootPE = (from adminpe in dataContext.PermissionEntities
where adminpe.Name == "TelesageAdmin"
select adminpe
).FirstOrDefault();
}
newSession.Participant = (from pids in dataContext.ParticipantIdAliasLookups
where pid == pids.AliasId && pids.RootPermissionEntity.Id == rootPE.Id
select pids.Participant).FirstOrDefault();
if (newSession.Participant == null)
{
Participant newParticipant = new Participant();
ParticipantIdAliasLookup newPidAlias = new ParticipantIdAliasLookup();
newParticipant.ParticipantDataJSON = "";
newPidAlias.AliasId = pid;
newPidAlias.RootPermissionEntity = rootPE;
newParticipant.AliasLookup = newPidAlias;
newParticipant.PermissionEntities.Add(currentUser.PermissionEntity);
newParticipant.PermissionEntities.Add(rootPE);
newSession.Participant = newParticipant;
dataContext.Participants.AddObject(newParticipant);
dataContext.ParticipantIdAliasLookups.AddObject(newPidAlias);
}
newSession.Assessment = assessment;
newSession.User = currentUser;
newSession.StartTime = DateTime.Now;
newSession.PermissionEntity = currentUser.PermissionEntity;
newSession.SetSessionData("engineversion", typeof(SmartQWeb.MvcApplication).Assembly.GetName().Version.ToString());
newSession.SetSessionData("assessmentname", newSession.Assessment.Name);
newSession.SetSessionData("assessmentversion", newSession.Assessment.Version);
newSession.SetSessionData("computername", Environment.MachineName);
newSession.SetSessionData("username", currentUser.Name);
dataContext.Sessions.AddObject(newSession);
newSession.SetSessionData("dxdata", JsonConvert.SerializeObject(newRuntime.RenderedDiagnoses));
newRuntime.formulaEngine = new FixedLengthFormulaEngine(newRuntime);
SessionLog newSessionLog = new SessionLog();
dataContext.SessionLogs.AddObject(newSessionLog);
newSession.SessionLog = newSessionLog;
dataContext.SaveChanges();
initMultiItemGroups(assessment, dataContext);
this.CurrentSessionId = newSession.Id;
this.CurrentUserId = currentUser.Id;
this.CurrentAssessmentId = assessment.Id;
newRuntime.Context = new RuntimeContext(this, dataContext);
this.GetAssessmentItems(assessment); //to populate the items dict
newRuntime.formulaEngine.InitializeContext(this.AssessmentItemsIds.Keys.ToList());
newRuntime.RenderedDiagnoses = new RenderedDiagnosisModel(newRuntime.Context.AdministeredItemOrderByName);
newRuntime.Context.Logger.WriteLog("Session started with assessment: " + newRuntime.Context.Assessment.Name + " with version: " + newRuntime.Context.Assessment.Version);
newRuntime.Context.Logger.WriteLog("Session started by user: " + newRuntime.Context.CurrentUser.Name + "for participant ID: " + newSession.ParticipantId);
return newRuntime;
}
//start from a previous existing session
public AssessmentRuntime StartAdministrativeSession(NetScidDbContainer dataContext, Assessment assessment, Session previousSession, User currentUser, string pid = "", string resumefromlatest = "false")
{
AssessmentRuntime newRuntime = new AssessmentRuntime(this, dataContext);
Session newSession = new Session();
Assessment sessionAssessment = assessment;
newSession.ParticipantId = previousSession.ParticipantId;
//THE OTHER ENTITIES BESIDES THE SESSION NEED TO BE DETATCHED AND RE-ATTACHED (BY BEING ADDED TO THE NEW SESSION)
assessment.PrepElements();
newRuntime.RenderedDiagnoses = new RenderedDiagnosisModel(newRuntime.Context.AdministeredItemOrderByName);
List<Response> prevresponses = previousSession.Responses.ToList();
if (sessionAssessment == assessment)
{
foreach (Response prevresponse in prevresponses)
{
newRuntime.Context.CachedAssessmentResponses.Add(prevresponse.Item.ItemName, prevresponse);
dataContext.Detach(prevresponse);
newSession.Responses.Add(prevresponse);
}
}
else
{
//the sessionAssessment is now the more up-to-date one so the responses pulled will have to be mapped, not just detatched and copied
foreach (Response prevresponse in prevresponses)
{
Dictionary<string, FixedLengthItem> newAssessmentItemNames = AssessmentHelper.GetAssessmentItems(sessionAssessment);
if (!newAssessmentItemNames.ContainsKey(prevresponse.Item.ItemName))
continue;
Response newResponse = new Response
{
NumericValue = prevresponse.NumericValue,
Item = newAssessmentItemNames[prevresponse.Item.ItemName],
ItemName = prevresponse.Item.ItemName,
AdministeredOrder = -1,
Value = prevresponse.Value
};
newRuntime.Context.CachedAssessmentResponses.Add(newResponse.Item.ItemName, newResponse);
newSession.Responses.Add(newResponse);
}
}
newSession.SessionDataJSON = previousSession.SessionDataJSON;
//DxData
newRuntime.RenderedDiagnoses =
JsonConvert.DeserializeObject<RenderedDiagnosisModel>(newSession.GetSessionData("dxdata"));
//inc session is =2 when the the session has been resumed by
previousSession.IncSession = 2;
newSession.SetSessionData("previoussession", previousSession.Id.ToString());
newSession.Participant = previousSession.Participant;
newSession.Assessment = sessionAssessment;
newSession.User = currentUser;
newSession.PermissionEntity = currentUser.PermissionEntity;
newSession.StartTime = DateTime.Now;
dataContext.Sessions.AddObject(newSession);
dataContext.SaveChanges();
newRuntime.formulaEngine = new FixedLengthFormulaEngine(newRuntime);
initMultiItemGroups(assessment, dataContext);
newSession.SetSessionData("continuedsession", newSession.Id.ToString());
this.CurrentSessionId = newSession.Id;
this.CurrentUserId = currentUser.Id;
this.CurrentAssessmentId = assessment.Id;
this.GetAssessmentItems(assessment); //to populate the items dict
this.SetUpPreviousResponses(newSession); //populates SSO responses
newRuntime.Context = new RuntimeContext(this, dataContext);
if (newRuntime.RenderedDiagnoses != null)
newRuntime.RenderedDiagnoses.reInitAdminOrder(newRuntime.Context.AdministeredItemOrderByName);
newRuntime.formulaEngine.InitializeContext(this.AssessmentItemsIds.Keys.ToList());
return newRuntime;
}
//start from the SSO
public AssessmentRuntime ResumeSessionFromState(NetScidDbContainer dataContext)
{
AssessmentRuntime newRuntime = new AssessmentRuntime(this, dataContext);
newRuntime.formulaEngine = new FixedLengthFormulaEngine(newRuntime);
newRuntime.formulaEngine.InitializeSerializedContext(serializedJSContext);
newRuntime.Context = new RuntimeContext(this, dataContext);
if (this.CurrentAssessmentElementId != 0)
{
newRuntime.CurrentAssessmentElement = (from ae in dataContext.AssessmentElements
where ae.Id == this.CurrentAssessmentElementId
select ae).FirstOrDefault();
newRuntime.CurrentAssessmentElement.GetNewRuntime(newRuntime.Context);
}
return newRuntime;
}
private void GetAssessmentItems(Assessment assessment)
{
var fixedLengthElements = (from elements in assessment.AssessmentElements
where elements is FixedLengthBlock
select elements);
foreach (FixedLengthBlock elemblock in fixedLengthElements)
{
foreach (FixedLengthItem item in elemblock.ItemBank.Items)
{
item.aeRef = elemblock;
AssessmentItemsIds.Add(item.ItemName, item.Id);
}
}
}
private void SetUpPreviousResponses(Session newSession)
{
//put response IDs by itemname into this dictionary
SessionResponseIds = (from response in newSession.Responses
select response).ToDictionary(key => key.ItemName, value => value.Id);
}
}
And the RenderablePersistanceObject looks like this:
public class RenderablePersistanceObject
{
public int primaryItemId;
public int MultiItemGroupId;
public bool isMultiItem;
}
You can't serialize a straight Dictionary like that. You can take a look at this MSDN article for suggestions on implementing. If you don't truly need a Dictionary, perhaps List> might work better?
As Erik pointed out, it was the lack of the adorner [Serializable()] on that other object that caused the problem. Adding it solved my issue.

Resources