I am working with an asp MVC5 app with entity framework 6 and want to create an object that has a navigation property like so:
public class widget
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Category Category { get; set; }
}
where the property Category is another entity in the database model. When I implement the CRUD functionality, I create a view model like so:
public class EditWidgetViewModel
{
public List<SelectListItem> Categories { get; set; }
public int CategoryId { get; set; }
}
and the contents of the selectList goes to a dropdown in the html form. Next, the CategoryId gets posted back to the server when the user submits the form. From there, I am currently doing something like the following to save changes:
var dbWidget = new Widget
{
Name = model.Name,
Category = db.Categories.Find(CategoryId)
}
db.Widgets.Add(dbWidget);
db.SaveChanges();
So my question is the following: can I assign the category navigation property without doing another DB lookup with db.Widgets.Find(WidgetId) - I already know the id number that should go in the Category_Category_Id column in the Widgets table of the database without doing a lookup. Also, it seems like if you have something with five or so navigation properties that this would be a significant performance problem with a round trip for each of those.
You can create a new entity with a constructor, assign it a proper id
then use dbContext's Entry().State like this:
Category category = new Category { Id = CategoryId };
db.Entry(category).State = EntityState.Unchanged;
var dbWidget = new Widget
{
Name = model.Name,
Category = category
}
db.Widgets.Add(dbWidget);
db.SaveChanges();
Related
I have a many to many relationship in my code first.
public class Post
{
public int Id { get; set; }
public ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public int Id { get; set; }
public ICollection<Post> Posts { get; set; }
}
modelBuilder.Entity<Post>().HasMany(c => c.Tags).WithMany(a => a.Posts);
If i have a PostId and a TagId , How i can insert relationship with single query in entity framework (Without load Post or Tag and add relationship to that)
This is one of the drawbacks of the implicit junction table.
Still it's possible to do what you are asking by creating two "stub" entities, attach them to the context (this telling EF that they are existing), and adding one of them to the collection of the other:
using (var db = new YourDbContext())
{
var post = db.Posts.Attach(new Post { Id = postId });
var tag = db.Tags.Attach(new Tag { Id = tagId });
post.Tags = new List<Tag> { tag };
db.SaveChanges();
}
Due to the hack-ish nature of above technique, make sure to use it only with short lived contexts specifically allocated for the operation.
If I understood your question correctly, you want to ignore the insertion of navigation property. You can change state of the collection property as 'UnChanged' to avoid insertion of the property.
It will looks like;
_context.Posts.Add(post);
_context.Entry(post.Tags).State = EntityState.Unchanged;
_context.SaveChanges();
I have a model that is defined in EF database first edmx. From there I expose some tables and views (mainly views). As it's possible to augment the EF model with OData, how could I add a navigation property of a complex type to another EF and OData exposed type?
Currently I define a partial class and add the properties and attributes using them. But it looks like it's possible to add the desired properties with OData's modelbuilder functionality too, or perhaps better yet, first use ODataConventionModelBuilder and then augment the results. Alas, I'm unable to stitch together a working example from the existing API documentation and examples I've found.
Here's the code
//This class is generated from a view by EF (edmx)...
public partial class AccountView
{
public System.Guid Id { get; set; }
public int CompanyId { get; set; }
}
//Here's augmenting the EF generated view with some additional data...
[MetadataType(typeof(AccounViewMetaData))]
public partial class AccounView
{
//This is added here explicitly. AccountView itself exposes just
//a naked key, CompanyId.
public virtual Company Company { get; set; }
//This is just in case...
public class AccounViewDomainMetaData
{
//This is to add a navigation property to the OData $metadata. How to do this
//in WebApiConfig? See as follows...
[ForeignKey("Company")]
public int CompanyId { get; set; }
}
}
//This is an EF generated class one from an edmx..-
public partial class Company
{
public Company() { }
public int CompanyID { get; set; }
public string Name { get; set; }
}
//How to add a navigation property from AccountView to Company so that it'd become
//possible to call http://example.com/Accounts?$expand=Company and http://example.com/Accounts(1)?$expand=Company ?
var builder = new ODataConventionModelBuilder();
var companySet = builder.EntitySet<Entities.Company>("Companies");
var accountSet = builder.EntitySet<Entities.AccountView>("Accounts");
accountSet.EntityType.HasKey(i => i.Id); //EF has hard time recognizing primary keys on database first views...
//How to hide this from the result if there's a way to create a ?$expand=Company navigation property?
//accountSet.EntityType.Ignore(i => i.CompanyId);
This is related to my other question regarding OData and models.
I am new to MVC
I have an Employee POCO like this
[PetaPoco.TableName("tblEmployee")]
[PetaPoco.PrimaryKey("EmployeeId")]
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public string City { get; set; }
public int DepartmentId { get; set; }
}
Department Id is actually a foreign key coming from Table tblDepartment. So I want to limit the value of DepartmentId in creating new Employee as the values existing in table tblDepartment(column : Id ).How to do this?
Existing code in Create View
#Html.EditorFor(model => model.DepartmentId)
Why not use DropDownList for Department selection?
#Html.DropDownListFor(m => m.DepartmentID, Model.DepartmentList, string.Empty)
public class Yourclass
{
public SelectList DepartmentList { get; set; }
public Yourclass()
{
FillModel();
}
internal void FillModel()
{
this.DepartmentList = GetDepartmentListFromDb();
}
}
Usually reference integrity is managed at a database level.
Make the relationship on your database between Employees and Departments to enforce reference integrity, so if you try to save an Employee whose department id don't exist on Department table it should return an integrity error.
Capture this error on your saving controller and return it to user.
Also, it will help if you allow your users only to load data from a limited list loaded with existing data on the database, as it is suggested in comments.
However this could not be enough, as a valid value could be deleted from database between the moment it was posted to the list in your form and the moment the form is sent to the controller to save data.
I've created an MVC project using entity framework code first. My model is simply a form that gathers information.
public class Application
{
public int Id { get; set; }
public string FirstName { get; set; }
public string MiddleInitial { get; set; }
public string LastName { get; set; }
public int SSN { get; set; }
public DateTime DOB { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State {get; set; }
public string Zip { get; set; }
public int HomePhone { get; set; }
public int BusinessPhone { get; set; }
public int MobilePhone { get; set; }
}
My goal is to create a drop down list with all of the states, but I'm finding this to be very difficult given that I've already created the database via scaffolding with views and controllers. Is there a simple way to do this and tie it in with the existing database? I've searched for almost the entire day with no luck. An overview/explanation of what to include for the model/controller/view would be amazing!
Update: I've created a new model named "State" with properties "Id" and "StateName" and have created some states in the database. In my "Application" controller inside the Create action method I have:
Controller
public ActionResult Create()
{
ApplicationDbContext db = new ApplicationDbContext();
this.ViewData["Id"] = new SelectList(db.States.ToList(), "Id", "StateName");
return View();
}
View
#Html.DropDownList("Id")
Now the problem is I'm getting this error " There is no ViewData item of type 'IEnumerable' that has the key 'Id'." Would really appreciate help!
Its quite simple. Add an IEnumerable<SelectListItem> property to your model(Here I suggest you make a ViewModel that can have the exact same properties as Application with the below code included as a property). Then you just need to build the list and send it to your view
public IEnumerable<SelectListItem> States{ get; set; }
I will assume you want to retrieve the State values from the db. Here is how you will do it:
private IEnumerable<SelectListItem> GetAllStates()
{
IEnumerable<SelectListItem> list = from s in db.Applications
select new SelectListItem
{
Selected = false,
Text = s.State,
Value = s.State
};
return list;
}
Or this...
private IEnumerable<SelectListItem> GetAllStates()
{
IEnumerable<SelectListItem> list = db.Applications.Select(s => new SelectListItem
{
Selected = false,
Text = s.State,
Value = s.State
});
return list;
}
Then do something like this in your action:
var app = new Application
{
States = GetAllStates()
};
return View(app);
Then finally, use Razor on the view to display the Dropdown list like this
#Html.DropDownListFor(m => m.State, Model.States, "--Select a State--")
The 1st parameter is the property of the model to update, the 2nd is the list of data, and 3rd is the default message that will be displayed
Hope this helps.
Create a data layer that retrieves a list of what you want. Then use EF to get all the states.
//assuming you have a table of states..
var states = db.States();
The states table should be a Unique list of states.
var selectList = new List<SelectListItem>();
foreach(var thing in states){
//if you got everything, thus the ID field for the value...
selectList.Add(new SelectListItem {Text =thing.State, Selected = false, Value = thing.ID);
}
Make sure in your Viewmodel class that selectlist is a public property.....and set to what you did above. You also need to provied a string for the view selection post back.
StatesSelectList = selectList;
public IEnumberable<SelectListItem> StatesSelectList {get;set;}
public string SelectedState {get;set;}
In your view, do this:
#Html.DropDownListFor(p=>Model.SelectedState, Model.StatesSelectList)
Very simple Code step by step
1) In Entity Framework Class
var productsList = (from product in dbContext.Products
select new ProductDTO
{
ProductId = product.ProductId,
ProductName = product.ProductName,
}).ToList();
2) In Controller
ViewBag.productsList = new EntityFrameWorkClass().GetBusinessSubCategoriesListForDD();
3) In View
#Html.DropDownList("Product_ProductId", new SelectList(ViewBag.productsList, "ProductId", "ProductName"), new { #class = "form-control" })
OR
#Html.DropDownListFor(m=>m.Product_ProductId, new SelectList(ViewBag.productsList , "ProductId", "ProductName"), new { #class = "form-control" })
I assume there is a States model that has a Id and a StateName property.
Change to the list to ViewData["State"] to ensure easy binding on POST.
Id is the value that will be sent in the POST data ie.. State = Id. The StateName is what will be displayed in the Select list. So for your model this is not correct as State is a string. So needs to be
this.ViewData["State"] = new SelectList(db.States.ToList(), "StateName", "StateName");
Then in your view
#Html.DropDownList("State")
I'm facing an issue when inserting records with Many to Many navigation property,
I have these models
//ModelBase
public class ModelBase
{
[Key]
public long Id { get; set; }
}
//Book
public class Book : ModelBase
{
public string Title { get; set; }
public virtual ICollection<Author> Authors { get; set; }
public virtual ICollection<PublishedBook> PublishedBooks { get; set; }
}
//Author
public class Author : ModelBase
{
public string Name { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
And DTOs called BookDto and AuthorDto for transfer data between layers
From the controller I fill data to DTOs and call Create method (which is in separate layer) to Save data to database,
BookDto bookDto = new BookDto()
{
Id = model.Id,
Title = model.Title,
Authors = model.AuthorIds.Select(c => new AuthorDto { Id = c }).ToList()
}
using (ServiceFactory facory = new ServiceFactory())
{
factory.Book.Create(bookDto);
}
In the Create method I map DTOs with POCOs using ValueInjector
public void Create(BookDto bookDTO)
{
Book book = new Book();
book.InjectFrom<DeepCloneInjection>(bookDTO);
bookRepository.Add(book);
unitOfWork.Commit();
}
And it calls Add method in Genaric Repository Base
public virtual void Add(T entity)
{
dbset.Add(entity);
}
This inserts data to Books table and BookAuthors tables appropriately BUT inserts new record into the Authors table even if I pass Authors which has existing AuthorIds for Book.Authors from the controller.
Any Idea how to fix this?
You are missing to attach the existing authors to the context. You could do it like so:
public void Create(BookDto bookDTO)
{
Book book = new Book();
book.InjectFrom<DeepCloneInjection>(bookDTO);
foreach (var author in book.Authors)
authorRepository.Attach(author);
bookRepository.Add(book);
unitOfWork.Commit();
}
where the generic Attach methods is just implemented calling dbset.Attach(entity);. I am assuming that all repositories (authorRepository and bookRepository) share the same context instance. Otherwise the above won't work.
The fact that the AuthorId already exists in the database doesn't matter. EF doesn't check if the Id exists by a query first. If you call Add on an object graph of detached entities it tries to insert the whole graph by generating INSERT statements for the parent and for all children. By attaching the children you effectively turn off the INSERT statements for those.