BlueshiftSoftware / EntityFrameworkCore

NoSQL providers for EntityFramework Core
Other
281 stars 57 forks source link

InvalidOperationException when using an entity without an Id #11

Closed yaseralnajjar closed 7 years ago

yaseralnajjar commented 7 years ago

Hi again,

I'm using a view model to update the owner data:

public class OwnerUpdateViewModel
{
    public SelectList OwnerType { get; set; }
    public string OwnerName { get; set; }

    public IOwner ToModel()
    {
        var result = OwnerHelper.GetOwner(OwnerType.SelectedValue.ToString());
        result.Name = OwnerName;

        return result;
    }
}

Get action:

public async Task<IActionResult> Update(ObjectId id)
{
    var car = await _context.Cars.SingleOrDefaultAsync(m => m.Id == id);
    if (car == null)
    {
       return NotFound("Invalid car id!");
    }

    return View();
}

It will throw an exception:

InvalidOperationException: The entity type 'OwnerUpdateViewModel' requires a primary key to be defined.

Only with this it will work is to add this property to the view model:

public ObjectId Id { get; set; }

(as you know, the usage of the view model is to eliminate the Id and show only the needed stuff)

*P.S: I realized the best way to figure out the issues of EFMongo library is to make a "semi" real-life-project. I started organizing things in that project, I'm gonna push it into github; it'll also serve as a good example for new comers.

yaseralnajjar commented 7 years ago

I've already uploaded the project here: https://github.com/0xRumple/EFMongoDemo

Some codes are broken for now... but there are other issues that the project will reveal.

crhairr commented 7 years ago

That particular error comes from EFCore when attempting to define a top-level Entity class that does not have a unique id. In particular, your DbContext directly references OwnerUpdateViewModel.

Your ASP.NET Core view models should never be directly referenced by your DbContext or any your EF Core Model's entities. They should serve only as a mapping layer between what MongoDB stores and returns, and how you want to display that information to the user. Your DbContext should have a DbSet whose entity element is the model for your MongoDB collection:

    DbSet<Owner> Owners { get; private set; } //actual Db entity, with an Id

And your controller actions should have something like this in them:

_context.Owners
    .SingleOrDefault(owner => owner.Id == updateOwnerId)
    ?.ToOwnerUpdateModel(); //extension method to copy entity properties to view model

Or via async:

(await _context.Owners
    .SingleOrDefaultAsync(owner => owner.Id == updateOwnerId))
    ?.ToOwnerUpdateModel(); //extension method to copy entity properties to view model
yaseralnajjar commented 7 years ago

Ooops... my bad! The dbset was added automatically when I added the view using the scaffolder.