efcore zero to one relationship cascade from principal - ef-core-2.0

in efcore 2.0 I'm trying to figure out how to configure this:
let say I have two table Upload and BlogPost and some more table that save their upload URL into upload. so the upload table doesn't need to have a FK to any other table that wants to save into it
class Upload{
public int Id{get; set;}
public string FileName{get;set;}
public string SavedPath{get; set;}
}
//but each blog post need to know about its own upload
class BlogPost{
public int Id { get; set; }
public int Content { get; set; }
public Upload Upload { get; set; }
}
according to this article, any table that holds the FK is the dependant and any table that hold the primary key is the principal.
but in this case, the BlogPost is the principal and Upload is the dependent. if I delete the BlogPost then the upload must delete, not the otherwise.
modelBuilder.Entity<BlogPost>(b=>{
b.HasOne(bp=>bp.Upload).WithOne().OnDelete(DeleteBehavior.Cascade);
})
according to OnDelete document, it delete the dependant if the the principal deleted, that in my case should be BlogPost, but because the Upload is not carrying any FK, efcore apply cascade wrongly.
my question is how do i make the efcore to get the BlogPost as the principal? or how should i approach this kind of scenario?

Related

Many to Many in EF core

I have the following models
public class Student
{
public int Id {get; set; }
public string Name {get; set; }
public ICollection<StudentToCourse> StudentToCourse {get; set; }
}
public class StudentToCourse
{
public int StudentId{get; set; }
public Student Student {get; set; }
public int CourseId{get; set; }
public Course Course {get; set; }
}
public class Course
{
public int Id {get; set; }
public string Name {get; set; }
public ICollection<StudentToCourse> StudentToCourse {get; set; }
}
I want to get a list of all COURSE per student ID, how do I go about doing that?
Short answer: SELECT and match on the Id values. Or programatically -
foreach(Student s in yourstudentList)
{
foreach(StudentToCourse stc in s.StudentToCourse)
{
if(stc.StudentId = s.Id)
//this is one you want, do something with it
}
}
Better answer:
First let's look at your model...
Imagine the underlying database structure.
Your middle table is known as a lookup.
You don't really need the entire Student, nor the entire Course object in it.
Also, your Course object does not need to know about either of the other objects.
If you can imagine three tables in your database you can see how you would logically connect a Student to a Course across the three tables.
Your model is still incomplete though. Within your application you still need a container, in this case a List courseIds. In this way your Student doesn't need to care about all the entries in the Student/Course lookup table, just the ones applicable to the particular Student. And you have an easily accessible object to pull data from, or send updates to, the database.
On initial population of the courseIds collection you would do a
SELECT FROM StudentToCourse where StudentId = x
You can then JOIN on your Course table to pull values such as the Course name.
If you find you need to do a lot of those look ups you may cache your Course list and lower your database traffic at the cost of some ram.
Each time you make a new student you would want to populate their list of course Ids and when committing the student to the database you would save your look up table.
This keeps your objects as lightweight as possible while maintaining their relationships.
There are various ways to write the SELECT statement in your dev environment, search them out and find one you like (or that matches your company's current practices) and get used to consistently using the same one. It will keep your code more readable and manageable.

How to configure one to zero and one to one relationship in fluent api with different PK and FK

I am a beginner and learning fluent API from tutorials. I read the some of the already given SO solution about 1-0, 1-* but I could not understand them properly. I simply want to use fluent API to set up One to One and One to Zero Relationship provided no convention followed.
House and Room (terrible example)
Requirement:
1. One House can have zero room or ONE room MAX.
2. One Room must be inside a house.
3. If a room is deleted House SHOULD NOT Get Deleted
4. If a House is getting deleted and there is a room then
User should first delete the room then only House should be allowed to be deleted.
public class House
{
public int HouseId { get; set; }
public string HouseName { get; set; }
public virtual Room Room { get; set; }
}
public class Room
{
public int RoomId { get; set; }
public string RoomName { get; set; }
public int HouseId { get; set; }
public virtual House House { get; set; }
}
So, Room cannot exist w/o a House but a House can exist w/o a room. Also, in case if a House has room, it can only have one.
dBModelBuilder.Entity<House>()
.HasOptional(f => f.Room)
.WithRequired(s => s.House);
I saw some solution but they are telling to set the PK and FK same. But I don't want to do that. Is there a way to achieve what I want w/o setting PK and FK same. I DO NOT want the HouseID to be PK of my Room class. Also in my case principal is House and Dependent is Room. Do I need to add soemthing like "Optional Dependent" or "Optional Principal". Can some one please guide me I am a beginner.
Also, Do I need go remove navigation property from any of my MODELS? Is that extraneous?
How to tell EF to use HouseId of Room class as FK.
Also in my case principal is House and Dependent is Room
Then you are on the right track with
modelBuilder.Entity<House>()
.HasOptional(f => f.Room)
.WithRequired(s => s.House)
because in EF one-to-one relationship the required side is always the principal. You need to specify the principal/dependent only when both sides are required or both sides are optional.
The problem is your requirement of using different FK in the dependent entity than the PK, which is the default EF model for such relationship (so called Shared Primary Key Association), and is supported well. From the other side, one-to-one FK association is supported, but with some limitations. More specifically, explcit FK property like HouseId in your Room is not supported - there is no HasForeignKey fluent method for this type of configuration, and it's by purpose. I can't say why, but it's a fact. If you try playing with [ForeignKey] attribute, you'll get quite unexpected results, so don't do that.
Remove that property:
public class Room
{
public int RoomId { get; set; }
public string RoomName { get; set; }
public virtual House House { get; set; }
}
and use Map fluent API with MapKey to specify the FK column name:
modelBuilder.Entity<House>()
.HasOptional(f => f.Room)
.WithRequired(s => s.House)
.Map(c => c.MapKey("HouseId"));
This will give you the desired database table design (although there is no way to constrain HouseId column to be unique, except manually editing the migration).

Tracking a user by GUID or by an integer in identity 2

As of Identity 2 they have switched from a id with an integer value (ex. 1,2,3,4,...) to a id with a nvarchar value that stores the id as some long string like
a234vt-23sdlj23klj-34jkh34jh34-23jk4jh2
If I'm creating an object that will have a single owner belonging to the person logged in, so I need to attach the user id to it, should I use this new id from Identity 2 or should I try and create some other value like an integer and put it into the aspnetusers table? Does it really matter, all I'm doing is fetching Gift object by owner(userid) and displaying/modifying gift objects on a form.
here is an example of my product object
public class Gift
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Category> Categories { get; set; }
public int Rating { get; set; }
public GiftStatus Status { get; set; }
Public string UserId {get; set; //where userid is the id of the user that owns this object, should it be 3kj23jh3-h3hk1jh2-khj2h34l1b-n22g35l ???
}
There is no problem with using the Guid UserId to reference a user, if that's what you're concerned about. Unless you have a specific reason for not wanting to use a Guid as the UserId, I would suggest you just use the default behavior in order to simplify implementation. Having two separate Ids to keep track of a user sounds needlessly complicated, I wouldn't recommend that path.
If you do have a good reason for requiring an Int as the primary key instead of a Guid, you might take a look at this: http://blogs.msdn.com/b/webdev/archive/2014/03/20/test-announcing-rtm-of-asp-net-identity-2-0-0.aspx (scroll down to the "Make the type of Primary Key be extensible for Users and Roles" section). This page has a link to an example project which shows you how to use an Int as the PK. It also mentions that this extension can be used to migrate applications which use Int PKs to the new Identity 2.0.
Here's another article that may be helpful: http://www.codeproject.com/Articles/777733/ASP-NET-Identity-Change-Primary-Key

With MVC/Entity Framework, how does one manage housekeeping tasks in the view?

My understanding is that only one model can be passed to the view at a time. The problem with this that I see is that I am being forced to pass the Entity Framework model, and not any model that will manage housekeeping in the view. Here is what I mean:
You need to make a page that allows someone to submit Cars to the database. Along with the form fields (e.g. CarName, CarMake, CarYear) you also need a checkbox at the bottom of the page called "Remember Values" which when checked will "remember" the form values when the user clicks the Submit button at the bottom, so when they return all of their form data is still in the form fields. Needless to say, this Remember Values variable is not part of the Entity Framework model- it is just a housekeeping variable for use in the view.
How would you guys handle adding this checkbox? It feels like it should be part of the model, but I can't send two models to the view. Am I just looking at this issue wrong? What would you recommend?
.NET 4.5/MVC 5/EntityFramework 6
This is a good situation to be using ViewModels.
Build your ViewModels with all properties that you'd want to send/retrieve to/from your view. For example:
EF Entity
public class Car {
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual string Make { get; set; }
public virtual string Year { get; set; }
}
View Model
public class AddCarViewModel {
public Car Car { get; set; }
public bool RememberValues { get; set; }
}
Controller
public class CarController : Controller {
// Constructor....
public ActionResult Add() {
var vm = new AddCarViewModel();
return View(vm);
}
[HttpPost]
public ActionResult Add(AddCarViewModel vm) {
if (ModelState.IsValid) {
_carService.Save(vm.Car);
}
return View(vm);
}
}
Another good approach is to create Domain Transfer Objects, which are POCO classes to hold data that is transferred through the pipes. For example, in your business layer you may want to audit any changes to your Car model. So you may have properties like CreatedBy, CreatedDate, UpdatedBy, UpdatedDate, etc. (These properties are generally never displayed to the end-user but are important to store).
So you'd create the following classes:
public class Car {
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual string Make { get; set; }
public virtual string Year { get; set; }
public virtual User CreatedBy { get; set; }
public virtual User UpdatedBy { get; set; }
public virtual DateTime CreatedDate { get; set; }
public virtual DateTime UpdatedDate { get; set; }
}
public class CarDTO {
public Guid Id { get; set; }
public string Name { get; set; }
public string Make { get; set; }
public string Year { get; set; }
}
and you can use a library such as AutoMapper to map properties from Car -> CarDTO:
var car = _carService.GetCarById(id);
var carDTO = Mapper.Map<Car, CarDTO>(car);
This way, you can choose which properties you want exposed to your views by utilizing DTO's.
I always create an additional model that I can convert to and from between the EF model.
This additional model gets passed to the View and holds al the neccesary properties like CarName, Carmake, CarYear, Remember and probably most importantly, the Id of that particular object.
So when the user submits, this model gets passed to the Post method where you can extract all the required properties. You fetch the database model using the Id from your DbContext and update the properties with the values that were just passed through.
Technically you can send two models to the view, if the model is actually something like a Tuple:
#model Tuple<SomeEFModel, SomeViewModel>
Though that's kind of ugly. And if you're creating a view model anyway you might as well just make it a composite of the Entity Framework model. Something like:
public class SomeViewModel
{
public SomeEFModel EFModel { get; set; }
public string SomeOtherProperty { get; set; }
// other stuff...
}
Then just build an instance of that in the controller and send it to the model:
#model SomeViewModel
You could even just de-couple the EF model and the view model entirely, creating a custom view model that has everything for that view and then translating to/from that and the EF model at the controller level. Ultimately it comes down to what implementation looks cleaner and is easier to maintain, which can differ from one context to another.
Edit: Another option, if the models get unwieldy for whatever bits of the framework you're relying on, could be to separate your outgoing and incoming models. For pushing data to the view, you can use the composite view model above. But then when the data comes back from the view just use a normal Entity Framework model and a couple of additional parameters for your additional fields:
public ActionResult Edit(int id)
{
// build the view model with the EF model as a property
return View(someViewModel);
}
[HttpPost]
public ActionResult Edit(SomeEFModel model, string someOtherProperty)
{
// here you have an EF model from the view like normal
// plus the additional property (however many you need)
// you can even create a separate view model to collect the other properties
// as long as the names are well defined, the model binder should build both
}
First, you absolutely should NOT be passing your EF models directly to your view, and you should certainly NOT be posting directly to your EF models. This is essentially taking untrusted, unsanitized input and directly writing it to your data model with only bare minimal validation.
While this may work with simple models with no security or other ramifications, imagine a situation where you allowed a user to edit his profile information. Further, imagine that in his profile you also stored information relating to his subscription information. A specially crafted submit could alter his subscription information and give himself free access to your site, or worse...
Instead, you use view models. Apart from the security aspects, view models are good because other than in very simple CRUD style sites, your views requirements are typically different from your data models requirements. For instance, a particular field might be nullable in your data model, but you might want to make it required in your view. If you pass your model directly, then you can't do that easily.
Finally, Aggregate view models aggregate many different submodels to provide an overall model for the view, which is what you are getting at. You would then use a service layer, repository, or business logic layer to translate your view model to your data model, massaging data or applying logic as needed.

EF6 Help in defining the foreign key of my model (optional foreign key & conflicting multiplicities)

I am new to EF6 and fluent API, but I really tried all the possibilities before trying to bother you here but I am kind of stuck now.
I have the following model:
public class Event
{
public int Id { get; set; }
[Required]
public string Label { get; set; }
}
public class Address
{
public int Id { get; set; }
[Required]
public String City { get; set; }
}
Here's what I am trying to do:
The event should have a required field Address
The address can be used in an event or in another class (let's call it Person for example). Therefore, it shouldn't be directly be bound to the Event class
When deleting an Event (or Person), I want to cascade and delete the linked Address
I tried many possibilities in Fluent API by looking at the documentations and searching on the net. Despite that, I kept having various issues: conflicting multiplicities, inconsistent model or not being able to cascade on delete.
Could someone please help? I am really stuck! :)
Thank you!
!!!UPDATE!!!
As requested, here's the best solution I could find:
public class Event
{
public int Id { get; set; }
[Required]
public string Label { get; set; }
[Required]
public virtual Address.Address Address { get; set; }
}
public class Address
{
public int Id { get; set; }
[Required]
public String City { get; set; }
}
And the fluent code as follows:
modelBuilder.Entity<Event>()
.HasRequired(e => e.Address)
.WithOptional()
.Map(e => e.MapKey("AddressId"))
.WillCascadeOnDelete(true);
This implementation adds a foreign key in the Event table to the Address table. However, the cascade on delete will work in the following direction: deleting the Address will delete the Event. Unfortunately, I am trying to accomplish the opposite: I want the Address to be deleted only when the Event is!
Thank you again :)
Your requirements are fighting each other.
Cascade delete is from principal to dependent, so when an Address should be deleted when an Event is, the Event must be the principal (the entity to which a foreign refers).
If more than one class must have an Address, the other classes should refer to Address, which makes Address the principal.
There is an alternative. Address could have two foreign keys, to Person and to Event. But this is not very appealing, because the keys must both be nullable and there is no database constraint to enforce that they are mutually exclusive.
Another alternative is to make Address a complex type, so both the Event and the Person tables will have all address columns. Of course this is not well normalized, and you can't handle Addresses as separate entities, but at least there is no cascade issue, because an address is part of the Event record.
I think your best option is to have several classes refer to Address and write logic to delete orphan addresses.

Resources