I have a question about Entity Framework Core related to the way EF Core assigns referenced objects to the model. For example this code:
try
{
List<Application> ApplicationList = await _dbContextDigitVP.Applications
.FromSqlRaw("Exec dbo.GetApplication {0}", id)
.ToListAsync();
if (ApplicationList.Any())
{
Application Application = ApplicationList.First();
await CategoryRepository.GetCategoryByApplicationIdAsync(Application.Id);
await PromoterRepository.GetPromoterByApplicationIdAsync(Application.Id);
await ApplicantRepository.GetApplicantByApplicationIdFullAsync(Application.Id);
await SepaRepository.GetSepaByApplicationIdAsync(Application.Id);
await VerificationRepository.GetVerificationByApplicationIdAsync(Application.Id);
await ApplicationStateRepository.GetAllApplicationStatesByApplicationIdAsync(Application.Id);
await ApplicationCommentRepository.GetApplicationCommentByApplicationIdAsync(Application.Id);
await ConfirmationRepository.GetConfirmationByApplicationIdAsync(Application.Id);
await HistoryRepository.GetHistoriesByApplicationIdAsync(Application.Id);
await TaxRepository.GetTaxByApplicationIdAsync(Application.Id);
return Application;
}
return null;
}
catch (Exception ex)
{
Logger.LogError(ex.ToString());
return null;
}
Now what these other repository functions do is essentially the same as how the variable ApplicationList gets set by executing a stored procedure directly on the database.
When I go through my code with the debugger then I can see that the Category of an Application gets set after the code inside the GetCategoryByApplicationIdAsync gets executed. I do not assign any referenced property anywhere in my code.
Is this normal behaviour of the framework or should I be alarmed about this?
Related
My main page has an ObservableCollection (defined in a ViewModel) holding objects (defined in a Model) and I have other pages, one that shows details and one to add a new object. I use the MVVM CommunityToolkit.
I can pass one object from the collection to the details page:
[RelayCommand]
async Task GoToDetails(DailyScrum scrum)
{
if (scrum is null)
return;
await Shell.Current.GoToAsync($"{nameof(View.ScrumDetailsPage)}", true,
new Dictionary<string, object>
{
{"Scrum", scrum }
});
}
However, I am not able to pass the whole collection to the "Add" page, add the new object and pass it back:
[RelayCommand]
async Task AddNewScrum(ObservableCollection<DailyScrum> scrums)
{
if (scrums is null)
return;
await Shell.Current.GoToAsync($"{nameof(View.AddScrumPage)}", true,
new Dictionary<string, object>
{
{"Scrums", scrums }
});
}
How can I do this? Or is it a wrong attempt to pass around the collection? Can I write access the collection from another viewmodel?
If the ObservableCollection's size is small, you can try to use the Newtonsoft.Json package to convert it to string and then pass it. Such as:
var jsonstring = JsonConvert.SerializeObject(scrums);
Shell.Current.GoToAsync($"{nameof(View.AddScrumPage)}?param={jsonstring}");
And convert the string to ObservableCollection with the following code:
ObservableCollection<DailyScrum> data = JsonConvert.DeserializeObject<ObservableCollection<DailyScrum>>(jsonstring);
If the collection's size is big, you can store the string in the Preferences. Such as:
var jsonstring = JsonConvert.SerializeObject(scrums);
Preferences.Set("Data", jsonstring);
And get the data in the AddScrumPage:
bool hasKey = Preferences.ContainsKey("Data");
var content = Preferences.Get("Data", string.Empty);
ObservableCollection<DailyScrum> data = hasKey ? JsonConvert.DeserializeObject<Model>(content) : null;
Update
You can try to use the Shell.Current.Navigation, it has the same effect as the Shell.Current.GoToAsync, such as:
[RelayCommand]
async Task GoToDetails(DailyScrum scrum)
{
if (scrum is null)
return;
await Shell.Current.Navigation.PushAsync(new View.ScrumDetailsPage(scrums))
}
In my app I have to be able to cancel uploads, I tried with threads cancelation but nothing happens, I think it is because I use DBContextFactory and I create a context for each uploaded file.
So I did this to save files in DB:
private async Task OnFilesDropped(FileUploadModel upload)
{
Uploads.Add(upload);
if (string.IsNullOrEmpty(upload.Error))
{
using var context = DbFactory.CreateDbContext();
upload.Context = context;
context.FileUploads.Add(upload);
upload.UploadCompletion = 45;
await context.SaveChangesAsync();
upload.UploadCompletion = 100;
}
}
and this in case of deleting a uploaded/uploading file:
private async Task DeleteUpload(FileUploadModel upload)
{
Uploads.Remove(upload);
await UploadsChanged.InvokeAsync(Uploads);
if (string.IsNullOrEmpty(upload.Error))
{
if (upload.UploadCompletion != 100)
{
await upload.Context.DisposeAsync();
}
else
{
using var context = DbFactory.CreateDbContext();
context.FileUploads.Remove(upload);
await context.SaveChangesAsync();
}
}
}
This way works because I dispose of the context, but I wonder if there is a better way of doing this? or if this solution could be problematic somehow?
Best Regards.
You should use a CancellationToken.
The SaveChangesAsync method on your context has an overload that can be provided a cancellationToken.
await context.SaveChangesAsync(cancellationToken);
If you already have a CancellationToken higher in the call stack, you can just pass that one down. Otherwise, you can create a CancellationTokenSource and use that to generate a cancellation token and then cancel it when appropriate.
[Route("testApp/{Id}")]
[HttpGet]
public async Task<List<string>> testApp(Guid Id)
{
return await new Heavy().WorkOnDictionary(Id);
}
I have created a stateful service and has implemented REST api calls in one of the call I want to access the IReliableDictionary but i cannot do that it asks for stateManager
I want to access IReliableDictionary inside WorkOnDictionary() function which is defined inside a class Heavy
public async Task<List<string>> WorkOnDictionary(Guid Id){
IReliableDictionary<string, List<string>> Dictionary = await this.StateManager.GetOrAddAsync<IReliableDictionary<string, List<string>>>("stringlistcache");
string monitorName=convertIdtoMonitor(Id);
using (var tx = this.StateManager.CreateTransaction())
{
var cachedValue = await Dictionary.TryGetValueAsync(tx, monitorName);
await tx.CommitAsync();
if(cachedValue.HasValue)
return cachedValue.Value
else
{
var res=await GetdetailFrmRemote(monitorName)
Dictionary.AddAsync(tx,monitorName,res);
return res;
}}
}
How should I initialize StateManager?
If I'm reading this correctly, it looks like you want to use IReliableStateManager in an MVC controller. In that case, simply inject it into your controller. ASP.NET Core has a nice DI system built in. The Service Fabric getting-started sample code shows you how to do this: https://github.com/Azure-Samples/service-fabric-dotnet-getting-started/tree/master/src/GettingStartedApplication/StatefulBackendService
While using MongoDB C# driver with WebApi I came to the following problem. When I want to read all documents (or even just one) from the database the repo's function will get the correct data but in WebApi the object returned from the repo causes a stack overflow. I suspect that I am doing something wrong with the way the objects are returned.
WebApi where the Repo's method is called:
// GET api/<controller>
public async Task<List<Event>> Get()
{
return await _repo.FindAll();
}
// GET api/<controller>/5
public async Task<Event> Get(string id)
{
Event e = await _repo.FindById(id);
return e;
}
And corresponding methods in the Repo:
public async Task<Event> FindById(string id)
{
Event e = await _collection.Find<Event>(x => x.ID == ObjectId.Parse(id)).FirstAsync();
return e;
}
public async Task<List<Event>> FindAll()
{
var filter = new BsonDocument();
List<Event> list = await _collection.Find(filter).ToListAsync();
return await Task<List<Event>>.FromResult(list);
}
Thanks for all the help in advance!
Edit: I found that when I return string from the function instead of Event the whole thing works.
What I think is making problems is the ID property in the Event.
The problem was that the Event had an ObjecId property. The JSON.Net doesn't know about that type. See the solution here: JSON.NET cast error when serializing Mongo ObjectId
In ADO.NET, ExecuteNonQuery() "For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command" (http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executenonquery.aspx)
In EF v1, context.SaveChanges() method returns "The number of objects in an Added, Modified, or Deleted state when SaveChanges was called." (http://msdn.microsoft.com/en-us/library/bb739065.aspx)
Please tell, when multiple entities (or single entity) are added or updated to context and context.SaveChanges() method is called, how to check if actual INSERT or UPDATE was successful.
Can we assume if there was NO exception that INSERT(s) or UPDATE(s) was successful ?
Thank You
Yes, if there is no exception you may assume that the statements executed successfully.
In EntityFramework, SaveChangesAsync() returns an int.
So you can check if it is > 0 or not.
If something happens with SaveChangesAsync() it will return the number of effected rows and this means if value > 0 then true. So simply, you can have below scenerio:
INSERT
public Task<bool> CreateEntity(Entity entity){
if(entity == null)
return false;
await _dataContext.Entities.AddAsync(entity);
var created = await _dataContext.SaveChangesAsync();
return created > 0;
}
UPDATE
public async Task<bool> UpdateEntity(Entity entityToUpdate)
{
if(entityToUpdate == null)
return false;
_dataContext.Posts.Update(entityToUpdate);
var updated = await _dataContext.SaveChangesAsync();
return updated > 0;
}
DELETE
public async Task<bool> DeleteEntity(int entityId)
{
var entity = await _dataContext.Entities.FindAsync(entityId);
if (entity == null)
return false;
_dataContext.Entities.Remove(entity);
var deleted = await _dataContext.SaveChangesAsync();
return deleted > 0;
}
And in your methods, now you can simply check if that change is success or not:
For a simple MVC scenerio:
public Task<IActionResult> CreateEntity(EntityModel model)
{
if(model == null)
return StatusCode(404);
var entity = new Entity
{
attribute1 = model.attribute1,
attribute2 = model.attribute3
};
var isCreated = await _entityService.CreateEntity(entity);
if(isCreated)
{
//do something and return a view.
}
else
{
//you can return a status code, or an error view.
}
}
You can do the same practice for Update & Delete
Maybe this is not direct answer to the question, but may help.
By default all commands are encapsulated in one DbTransaction when SaveChanges method is called (Julia Lerman, Programming Entity Framework). So, or all commands will be successfully executed, or neither. That's one way to know if insert, or update or delete was successful.
define variable SaveStatus
var SaveStatus=context.SaveChanges()
then you can know if the creation has been done by getting the value of SaveStatus=1
in the case of "SaveStatus=0" it means no record has been created or affected