FindOneAndUpdateAsync Intermittently Returning Null - .net-core

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.

Related

How do I prevent a duplicate entry for a Create/Edit Functionality in ASP.NET Core w/ EF?

I am trying to prevent a user from creating a form with a FirstName, LastName, and DOB that match an entry in the database and editing a form to match an existing entry. If you could also lead me to how I can show an error when this happens, that would be awesome.
My Model:
public class MRegForm
{
public int MRegFormId { get; set; }
[Display(Name = "First Name")]
public string FirstName { get; set; } = string.Empty;
[Display(Name = "Last Name")]
public string LastName { get; set; } = string.Empty;
public DateTime DOB { get; set; }
[I tried Index attribute. It did not work for me. I was able to create new duplicate forms with no issues.
[Index(nameof(FirstName), nameof(LastName), nameof(DOB), IsUnique = true)]
public class MRegForm
{
I also tried this. Same thing.
protected override void OnModelCreating(ModelBuilder modelbuilder)
{
base.OnModelCreating(modelbuilder);
modelbuilder.Entity<MRegForm>()
.HasIndex(x => new { x.FirstName, x.LastName, x.DOB})
.IsUnique();
}
public DbSet<MRegForm> MRegForm { get; set; } = default!;
I think that there is maybe a way to prevent this in the OnPostAsync()
This is my create OnPostAsync():
public async Task<IActionResult> OnPostAsync()
{
MRegForm.CreatorId = UserManager.GetUserId(User);
var isAuthorized = await AuthorizationService.AuthorizeAsync(User, MRegForm, RegFormOperations.Create);
if (isAuthorized.Succeeded == false)
return Forbid();
Context.MRegForm.Add(MRegForm);
await Context.SaveChangesAsync();
return RedirectToPage("./Index");
}
This is my Edit OnPostAsync():
public async Task<IActionResult> OnPostAsync(int id)
{
var mRegForm = await Context.MRegForm.AsNoTracking().SingleOrDefaultAsync(m => m.MRegFormId == id);
if (mRegForm == null)
return NotFound();
MRegForm.CreatorId = mRegForm.CreatorId;
var isAuthorized = await AuthorizationService.AuthorizeAsync(User, MRegForm, RegFormOperations.Update);
if (isAuthorized.Succeeded == false)
return Forbid();
MRegForm.Status = mRegForm.Status; // the Status is the current Status - Do Not Reset
Context.Attach(MRegForm).State = EntityState.Modified;
try
{
await Context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MRegFormExists(MRegForm.MRegFormId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MRegFormExists(int id)
{
return (Context.MRegForm?.Any(e => e.MRegFormId == id)).GetValueOrDefault();
}
}
You can try to download the entity from the database if exists and make changes to it or creating new one if not exist.
Your clients can always make new MRegForm in the form, but you add or update in the back and. Bether option will be to pass the existing MRegForm to the form and the client see and change all props he need.
public async Task AddOrUpdate(MRegForm input)
{
var mRegForm = await Context.MRegForm
.FirstOrDefaltAsync(x => x.FirstName == input.FirstName && x.LastName == input.LastName && x.DOB == input.YourDate);
if(mRegForm != null)
{
//Make changes on mRegForm
mRegForm.SomeProp = input.SomeProp,
...
}
else
{
var newMRegForm = new MRegForm
{
//Set all props you need
}
await this.Context.AddAsync(newMRegForm );
}
await this.Context.SaveCangesAsync();
}

Syncfusion TreeView doesnt show data

I have a list that matches the requirements that I get through the request from js.
Data from the request comes filled in, but the list is not displayed
< ejs-treeview id="treedata" created="created">
< e-treeview-fields dataSource="#Model.Items" id="LevelCode" parentId="ParentLevelCode" text="Name" hasChildren="HasChild"></e-treeview-fields>
< /ejs-treeview>
function created()
{
getCategories();
}
function getCategories() {
let treedata = document.getElementById('treedata').ej2_instances[0];
let request = new ej.base.Ajax(`/Category/GetAll`, 'GET');
request.send();
request.onSuccess = data => {
if (treedata.element !== undefined) {
let final = JSON.parse(data);
treedata.fields.dataSource = final.Categories;
treedata.dataBind();
treedata.refresh();
}
};
}
public class GetAllCategoriesHandlerResponseItem
{
public string Id { get; set; }
public string Name { get; set; }
public bool HasChild { get; set; }
public string LevelCode { get; set; }
public string ParentLevelCode { get; set; }
}
In TreeView component, the fields property has been provided to set or get the data source and other data-related information. You can use this property to dynamically change the TreeView component data source. But you need to specify the properties in its predefined structure to update the TreeView data source.
Check the below code snippet.
function getCategories() {
let treedata = document.getElementById('treedata').ej2_instances[0];
let request = new ej.base.Ajax(`/Category/GetAll`, 'GET');
request.send();
request.onSuccess = data => {
if (treedata.element !== undefined) {
let final = JSON.parse(data);
treedata.fields = {datasource: final.Categories, id:"LevelCode", parentId:"ParentLevelCode", text:"Name", hasChildren:"HasChild" };
treedata.dataBind();
treedata.refresh();
}
};
}
You can refer to the below link to know about the details.
https://www.syncfusion.com/kb/10135/how-to-refresh-the-data-in-ej2-treeview

Filter result from firebase ObservableCollection

I am trying to filter records from FirebaseDB in my Xamarin app.
Following code returns all records from DB:
internal ObservableCollection<MessageModel> GetMessages(Guid messageId)
{
var result = (firebase).Child("Messages").AsObservable<MessageModel>().AsObservableCollection();
return result;
}
But when I apply Where it returns 0 records event that se that I have records in DB that match MessageId
internal ObservableCollection<MessageModel> GetMessages(Guid messageId)
{
var result = (firebase).Child("Messages").AsObservable<MessageModel>().AsObservableCollection().Where(s => s.Id == messageId); ;
return new ObservableCollection<MessageModel>(result);
}
I suggest you can use the following code to query data from firebase.
public async Task<List<Person>> GetAllPersons()
{
return (await firebase
.Child("Persons")
.OnceAsync<Person>()).Select(item => new Person
{
Name = item.Object.Name,
PersonId = item.Object.PersonId
}).ToList();
}
public async Task<Person> GetPerson(int personId)
{
var allPersons = await GetAllPersons();
await firebase
.Child("Persons")
.OnceAsync<Person>();
return allPersons.Where(a => a.PersonId == personId).FirstOrDefault();
}
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
}

try to send a viewmodel to view

I have a asp mvc project :
Model:
public class TeamDto
{
public int TenantId { get; set; }
public string Name { get; set; }
public List<UserListDto> Users { get; set; }
}
public class AddTeamViewModel
{
public TeamDto Team { get; set; }
public List<UserListDto> AllUsers { get; set; }
}
Controller:
public async Task<IActionResult> AddOrEditTeam(int id = 0)
{
var users = await _userAppService.GetAllUsers();
var view = new AddTeamViewModel { Team = new TeamDto(), AllUsers = users.ToList() };
if (id == 0) return View(view);
var team = await _teamAppService.GetTeamById(id);
return View(new AddTeamViewModel { AllUsers = users.ToList() , Team = team });
}
View:
<h4 class="m-portlet__head-caption">#(Model.Team.Id > 0 ? "Edit Team" : "Add Team")</h4>
I can not undrestand why I get this error:
An unhandled exception occurred while processing the request.
NullReferenceException: Object reference not set to an instance of an object.
AspNetCore.Areas_App_Views_Team_AddOrEditTeam.b__22_1()
It seems you are posting the value and then trying to send the viewmodel to a view. In this case, try redirecting to action instead of showing the View.
public async Task<IActionResult> AddOrEditTeam(int id = 0)
{
var users = await _userAppService.GetAllUsers();
var view = new AddTeamViewModel { Team = new TeamDto(), AllUsers = users.ToList() };
if (id == 0) return RedirectToAction("YourViewName",view);
var team = await _teamAppService.GetTeamById(id);
AddTeamViewModel teams = new AddTeamViewModel{
AllUsers = users.ToList(),
Team = team
};
return RedirectToAction("YourViewName", teams);
}
This will solve your object reference issue.

Model returns null when the site first loads

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.

Resources