EF4: Save object children - entity-framework-4

in database I have these tables: Product, Request and RequestProducts.
RequestProducts is a table to link many Product to one Request.
Here is my code:
Product newProduct = new Product
{
Unity_ID = 3,
Quantity = 2,
Name = "toto",
AlreadyCurrency = true
};
Request newRequest = new Request
{
User_ID = 1,
CaseNumber = 1,
Draft = false
};
newRequest.Products.Add(newProduct);
_db.AddToProducts(newProduct);
_db.AddToRequests(newRequest);
_db.SaveChanges();
After execute that, in my database I get 1 product and 1 request. It's ok, but the link create with line newRequest.Products.Add(newProduct); is not created the table RequestProducts and yet EF4 understand the link by propose me the Products list in the Request object.
Is it possible to create this link only with this code?
Thank you!

You don't need an extra table for a one-to-many relationship. You'd only need that extra table for many-to-many relationships.
So, without knowing more specifics about what you're doing and why, I'd say it's working properly.
Ideally, your Request object would look something like:
public class Request
{
public int RequestId {get; set;}
//Other defining params here
public ICollection<Product> Products {get; set;}
}
Then your Product object would look like:
public class Product
{
public int ProductId {get; set;}
//other defining params
public int RequestId {get; set;} //your FK
public virtual Request Request {get; set;} //If you ever need the Product to be aware of the Request to which it is attached.
}

Assuming you use generated entities of EDMX, you should have a single RequestId FK column in your Product table.
Then after you generate the EDMX from DB, there will not be additional tables, so you should then be able to do it this way:
Request newRequest = new Request
{
User_ID = 1,
CaseNumber = 1,
Draft = false
};
Product newProduct = new Product
{
Unity_ID = 3,
Quantity = 2,
Name = "toto",
AlreadyCurrency = true,
Request = newRequest
};
//Redundant
//newRequest.Products.Add(newProduct);
_db.AddToProducts(newProduct);
//Redundant
_db.AddToRequests(newRequest);
_db.SaveChanges();
//Never forget - or wrap in a using statement
_db.Dispose()
Update
The case above is one2many (one request has many products)
becuase as soon as I set the Request (which is now a property of the Product) to newRequest, then when I add it to the context (or if it's already added), all the related entities of the whole graph will automatically be added too.
Read this carefully.

Related

Entity Framework - Relationships with Where

Following this answer I'm trying to do :
var posts = await _context.Posts
.Select(p => new
{
p,
Comments = p.Comments
.Where(c.Status == "approved")
})
.Include(p => p.Author)
.Include(p => p.Comments)
.SingleOrDefaultAsync(m => m.Id == id);
In order to get only "approved" comments with my post.
But I get the following error :
The name 'c' does not exist in the current context [ASPress]
So there's something I don't get right with the syntax
You have to define the identifier you're using in the lambda...
.Where(c => c.Status == "approved")
should fix it.
Having just read your comments, I believe it is based on the order you are using the LINQ methods. From Posts you are projecting to an anonymous type and that won't have what you want. Try to re-arrange it so it reads as follows:
var posts = await _context.Posts
.Include(p => p.Author)
.Include(p => p.Comments)
.Select(p => new
{
p,
Comments = p.Comments
.Where(c => c.Status == "approved")
})
Although you forgot to write your classes, it seems to me that you have a sequence of Posts. A Post has exactly one Author. Every Post has zero or more comments, and every comment belongs to exactly one Post: there are no comments that belong to nothing, there is a true one-to-many relation between Posts and Comments.
If you follow the guidelines for a one-to-many configuration, you'll have something similar to the following:
class Author
{
public int Id {get; set;}
// not certain, but very likely: every Author has zero or more Posts:
public virtual ICollection<Post> Posts {get; set;}
}
class Post
{
public int Id {get; set;}
// Every Post is posted by exactly one Author, using foreign key
public int AuthorId {get; set;}
public virtual Author Author {get; set;}
// Every Post has zero or more Comments:
public virtual ICollection<Comment> Comments {get; set;}
}
class Comment
{
public int Id {get; set;}
// every Comment belongs to exactly one Post using foreign key
public int PostId {get; set;}
public virtual Post Post {get; set;}
public string Status {get; set;}
}
Because I followed the entity framework naming conventions, this is enough to tell entity framework the primary keys, the foreign keys and the one-to-many relations. If you decide to deviate from the naming conventions, you'll probably need some attributes of fluent API, but the idea remains the same.
Back to your question
It seems to me that you want all Posts, together with their Author and only their approved Comments.
You use Include statements to get the data from the database. This is usually a waste of processing power, because you probably won't use all Post properties and all Comment Properties. It is better to explicitly name the Properties you plan to use.
var result = myDbcontext.Posts.Select(post => new
{
// query only the attributes you plan to use, for example:
Id = post.Id,
Title = post.Title,
Author = new
{
Id = post.Author.Id,
Name = post.Author.Name,
// no need to query the foreign Key Author.PostId, I already got that one in property Id
... // other author properties you need
},
Comments = post.Comments
.Where(comment => comment.Status == "approved")
.Select(comment => new
{ // again: query only the properties you plan to use:
Text = comment.Text,
...
}
.ToList(),
...
};
In my experience, if you configure your one-to-many relations properly you seldom need a join. Use the ICollections instead. Entity Framework will know that a join is needed for this.
However you can get the same results using a join:
var result = myDbContext.Posts
.Join(myDbContext.Comments.Where(comment => comment.Status = "approved"),
post => post.Id, // from every Post take the Id
comment => comment.PostId // from every Comment take the PostId
(post, comment) = new // when they match make a new object
{
Id = post.Id,
Title = post.Title,
CommentText = comment.Text,
}
etc. Note this is a real inner join. If you want the "Post with its comments" you'll need to do a GroupBy Id
Finally: if you only have a limited number of states, consider creating an enum for your status. This will prevent people from assigning "Approved" to the state instead of "approved", or worse: errors like "approved?" Internally in your database they will still be strings, but entity framework will parse them to proper enum values and you won't be able to put improper values in the database.

Asp.net mvc call entity db data using Id for that particular row

Hi and thank you for taking your time to read. I am having trouble calling from a db using entity framework for a particular row. Here is my code for controller.
public ActionResult MyAccount(CurrentAccount ca, SaverAccount sa, int id)
{
var model = db.CurrentAccounts.FirstOrDefault(_ => _.Id == id);
Session["Id"] = ca.Id;
Session["CurrentAccountNumber"] = ca.CurrentAccountNumber;
Session["CurrentBalance"] = ca.CurrentBalance;
Session["SaverAccountNumber"] = sa.SaverAccountNumber;
Session["CurrentBalance"] = sa.SaverAccountNumber;
return View(model);
}
My model is a edmx entity file and i can seem to retrieve some data to my locals but only from 1 table and i need data to be from multiple tables selecting a full row of data for a paricular Id then having this information visable on the same view. There is also a relation between id on both tables. Thanks :)
Here you have called wrong object because you are fetching data in model variable but calling from ca. please use as following
public ActionResult MyAccount(CurrentAccount ca, SaverAccount sa, int id)
{
var model = db.CurrentAccounts.FirstOrDefault(_ => _.Id == id);
Session["Id"] = model.Id;
Session["CurrentAccountNumber"] = model.CurrentAccountNumber;
Session["CurrentBalance"] = model.CurrentBalance;
Session["SaverAccountNumber"] = sa.SaverAccountNumber;
Session["CurrentBalance"] = sa.SaverAccountNumber;
return View(model);
}
You need to execute join query to get data from two models like following exmaple
Create a common class like follwing
public class datafrombothclass
{
public int Id { get; set; }
public String saveaccount_name { get; set; }
public String currrentaccount_name { get; set; }
}
Now use join query in entity framework to get data from both model in you case from CurrentAccount and SaverAccount.
See the bellow code example:
var frombothclass=(from a in Model.saveaccount join s in Model.currentaccountaccount
where a.Id=id
select new datafrombothclass{
Id=a.Id,
saveaccount_name=s.name,
currrentaccount_name=a.name
});
return View(frombothclass);
Hope you will get the solution.

Editing some properties of View Model in ASP.NET MVC

I'm using Entity Framework Database First approach. Let's say I have a model class called Product and that class has a NumberOfViews property. In the Edit page I pass an instance of the product class to the controller.
The problem is I can't add #Html.EditorFor(model => model.NumberOfViews) in the Edit page, because it's supposed that NumberOfViews is updated with every visit to the product page, and NOT by the website Admin.
And I can't add it as #Html.HiddenFor(model => model.NumberOfViews), because if the Admin Inspected the element, he can edit it manually.
Also If I try to programmatically set the value on the server-side (e.g., Product.NumberOfViews = db.Products.Find(Product.Id).NumberOfViews;), I get the following error:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
And if I don't add it to either the view or the controller, the value will be null, thus overriding any previous value.
So what should I do?
I have noticed a lot of people use the same model for their Entity Framework as they do for their MVC Controller. I generally discourage this practice. In my opinion, a database model is not the same as a view model.
Sometimes a view needs less information than what the database model is supplying. For example while modifying account password, view does not need first name, last name, or email address even though they may all reside in the same table.
Sometimes it needs information from more than one database table. For example if a user can store unlimited number of telephone numbers for their profile, then user information will be in user table and then contact information with be in contact table. However when modifying user profile, they may want to add/edit/delete one or more of their numbers, so the view needs all of the numbers along with first name, last name and email address.
This is what I would do in your case:
// This is your Entity Framework Model class
[Table("Product")]
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ProductId { get; set; }
public string Name { get; set; }
public int NumberOfPageViews { get; set; }
}
// This is the model you will use in your Edit action.
public class EditProductViewModel
{
public int ProductId { get; set; }
public string Name { get; set; }
}
public class ProductController : Controller
{
IProductService service;
//...
[HttpGet]
public ActionResult Edit(int productId)
{
var product = service.GetProduct(productId);
var model = new EditProductViewModel()
{
ProductId = product.ProductId,
Name = product.Name
};
return View(model);
}
[HttpPost]
public ActionResult Edit(EditProductViewModel model)
{
if (ModelState.IsValid)
{
var product = service.GetProduct(model.ProductId);
product.Name = model.Name;
service.Update(product);
}
// ...
}
}

Updating many-to-many relationship entity framework

I have problem with updating entites that have many-to many relationship. Below my User and category class:
public class User : IEntity
{
[Key]
public virtual long Id { get; set; }
private ICollection<Category> _availableCategories;
public virtual ICollection<Category> AvailableCategories
{
get { return _availableCategories ?? (_availableCategories = new List<Category>()); }
set { _availableCategories = value; }
}
}
public class Category : IEntity
{
[Key]
public long Id { get; set; }
/// <summary>
/// Full name or description of a category
/// </summary>
[StringLength(255)]
public string FullName { get; set; }
}
This is code snippet from my repository
public override void Edit(User user)
{
var dbUser = _context.Users.Include(x => x.AvailableCategories)
.Single(x => x.Id == user.Id);
var categories = _context.Categories;
dbUser.AvailableCategories.Clear();
foreach (var cat in user.AvailableCategories)
{
dbUser.AvailableCategories.Add(cat);
}
_context.Entry(dbUser).State = EntityState.Modified;
}
However the categories don't get updated. What EF does is insert empty rows into category table and sets relations to this new rows with user.
How can I update User so that I change only categories that already exist in the database?
User that I pass to Edit method has AvailableCategories with only Ids set (rest of properties are empty).
When you're doing something like posting back M2M relationships, you either must post the full object, as in every single property on those objects, or simply post a list of ids and then use those to query the associated objects back from the database. Otherwise, Entity Framework understands your purpose to be to update the properties on the objects as well, in this case with empty values.
Obviously the first option is quite unwieldy, so the second way is the preferred and standard way. Generally, for this, you'd want to use a view model so you could have a property like the following, that you would post into:
public List<long> SelectedCategories { get; set; }
But, if you insist on using the entity directly, you can get much the same result by simply doing:
var selectedCategories = user.AvailableCategories.Select(m => m.Id)
Once you have the ids:
var newAvailableCategories = _context.Categories.Where(m => selectedCategories.Contains(m.Id));
And then finally set that on your user:
dbUser.AvailableCategories = newAvailableCategories;
I notice you are also adding the user.AvailableCategories directly into dbUser.AvailableCategories. I've noticed when binding back complex objects from an MVC view that DB Entities are no longer attached to the DbContext. If you look at the entity, you can verify by checking dbContext.Entry(cat).State is "detached" (or something unexpected) I believe.
You must query those entities back out of the dbContext (possibly by using the returned cat.Id's). Or otherwise manually set the entities as "unchanged". And then add those "non-detached" items into dbUser.AvailableCategories. Please see Chris's answer as it shows with specific code how to get this done.
Also, I might use a linking entity. Possibly something like this:
public class UserCategory
{
public User User {get;set;}
public Category Category {get;set;}
}
And add it to DB context. Also, drop the linking lists in your current User and Category class. This way you can manipulate the UserCategory class (and DbSet) to manage your many-to-many relationship.

Should we use Data Repository pattern in MVC application using Entity Framework Code First approach?

I have developed many application now with Entity Framework Code First approach. In all of the I use Data Repository pattern. This Data Repository pattern queries over single entity at a Time. for e.g,
I have 2 models
Employee and Department
So to fetch all employees and department I would create 2 data repository instances. e.g
var empRepository = new DataRepository<Employee>();
var allEmployees = empRepository.GetAll();
var depRepository = new DataRepository<Department>();
var alldepartment = depRepository.GetAll();
Now, This pattern works great in most of the case. Now, When I want to perform join I can not do that in this pattern. I can perform join only after I have fetched all records of both entities and then i can use join on in-memory data. this creates extra overhead of 2 queries in my logic. Does any one have good pattern or solution that can be used with DataRepository pattern. Please suggest any alternatives to this pattern.
I actually just implemented a Join function in my generic repository just yesterday. It's a lot easier to do than how everyone's making it out to be, and you can use some cool features of Entity Framework while doing so.
To get started, I'm using a repository similar to what I wrote in this blog post. You'll notice that a number of the methods are returning IQueryable. This is no accident. IQueryable will allow to still use deferred execution, meaning that the query won't be run on the database until something forces it to (i.e. a loop or a .ToList() call). You'll see that, with putting the Join on this type of repository, that you won't end up needing to load up all the entities into memory to do the join, since all you'd be exposing is an IQueryable.
That being said, here's how I've got the Join in my repository, based off of the Queryable version of the Join method:
public IQueryable<TResult> Join<TInner, TKey, TResult>(IRepository<TInner> innerRepository, Expression<Func<T, TKey>> outerSelector, Expression<Func<TInner, TKey>> innerSelector, Expression<Func<T, TInner, TResult>> resultSelector) where TInner : class
{
return DbSet.Join(innerRepository.All(), outerSelector, innerSelector, resultSelector);
}
This, then, only makes one query to the database to get all of the information that you're requesting (again, since it only passes an IQueryable in the All() method).
with Repository pattern you can join two tables as in normal scenario.say you have defined your repository like this :
public interface IRepository<T>
{
T GetById(object id);
void Insert(T entity);
void Update(T entity);
void Delete(T entity);
IQueryable<T> Table { get; }
}
then you can join two table as following :
from p in _someRepository.Table
join p2 in _someOtherRepository.Table on p.FOO equals p2.BAR
with this approach there is no need to load all of table entries into memory.
Please take a look at the following links, in case you have not...
Social MSDN and
Forums.Asp.Net
There is a answer by #Link.fr which states as follows:-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace Repository
{
class Program
{
static void Main(string[] args)
{
// get all the informations about orders
CustomersRepository oCustomersRepository = new CustomersRepository();
ProductsRepository oProductsRepository = new ProductsRepository();
OrdersRepository oOrdersRepository = new OrdersRepository();
var query1 = oOrdersRepository.SelectAll().
Join(oCustomersRepository.SelectAll(),
order => order.CustomerId,
customer => customer.Id,
(order, customer) => new
{
MyOrder = order,
MyCustomer = customer
}).
Join(oProductsRepository.SelectAll(),
item => item.MyOrder.ProductId,
product => product.Id,
(item, product) => new
{
MyOrder = item.MyOrder,
MyCustomer = item.MyCustomer,
MyProduct = product }).
ToList();
foreach (var row in query1)
{
Console.WriteLine("query1 : {0} - {1}", row.MyCustomer.Name, row.MyProduct.Name);
}
Console.WriteLine("--");
Or
var query2 = (from order in oOrdersRepository.SelectAll()
join customer in oCustomersRepository.SelectAll() on order.CustomerId equals customer.Id
join product in oProductsRepository.SelectAll() on order.ProductId equals product.Id
select
new
{
CustomerName = customer.Name,
ProductName = product.Name
}).ToList();
foreach (var row in query2)
{
Console.WriteLine("query2 : {0} - {1}", row.CustomerName, row.ProductName);
}
Console.ReadKey();
}
}
The CLASS goes like:-
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Order
{
public int CustomerId { get; set; }
public int ProductId { get; set; }
}
public interface IRepository<T>
{
IList<T> SelectAll();
IList<T> SelectAll(Func<T, bool> expression);
}
public class CustomersRepository : IRepository<Customer>
{
public IList<Customer> SelectAll()
{
return new List<Customer>
{
new Customer{ Id = 1, Name = "Customer1"},
new Customer{ Id = 2, Name = "Customer2"},
new Customer{ Id = 3, Name = "Customer3"},
new Customer{ Id = 4, Name = "Customer4"}
};
}
public IList<Customer> SelectAll(Func<Customer, bool> expression)
{
return new List<Customer>
{
new Customer{ Id = 1, Name = "Customer1"},
new Customer{ Id = 2, Name = "Customer2"},
new Customer{ Id = 3, Name = "Customer3"},
new Customer{ Id = 4, Name = "Customer4"}
}.Where(expression).ToList();
}
}
public class ProductsRepository : IRepository<Product>
{
public IList<Product> SelectAll()
{
return new List<Product>
{
new Product{ Id = 1, Name = "Product1"},
new Product{ Id = 2, Name = "Product2"},
new Product{ Id = 3, Name = "Product3"},
new Product{ Id = 4, Name = "Product4"}
};
}
public IList<Product> SelectAll(Func<Product, bool> expression)
{
return new List<Product>
{
new Product{ Id = 1, Name = "Product1"},
new Product{ Id = 2, Name = "Product2"},
new Product{ Id = 3, Name = "Product3"},
new Product{ Id = 4, Name = "Product4"}
}.Where(expression).ToList();
}
}
public class OrdersRepository : IRepository<Order>
{
public IList<Order> SelectAll()
{
return new List<Order>
{
new Order{ CustomerId = 1, ProductId = 1},
new Order{ CustomerId = 1, ProductId = 2},
new Order{ CustomerId = 2, ProductId = 3},
new Order{ CustomerId = 3, ProductId = 4},
};
}
public IList<Order> SelectAll(Func<Order, bool> expression)
{
return new List<Order>
{
new Order{ CustomerId = 1, ProductId = 1},
new Order{ CustomerId = 1, ProductId = 2},
new Order{ CustomerId = 2, ProductId = 3},
new Order{ CustomerId = 3, ProductId = 4},
}.Where(expression).ToList();
}
}
}
May be it helps.
This is one of the reasons why repositories are a burden, especially generic repositories (one per entity type). If you try remain true to the pattern you always bump into some major problems.
You keep writing these clumsy join queries (linq). You can't really exploit the power of navigation properties. When queries get beyond simple this is a major pain.
So you're probably going to use navigation properties regardless. Now the question is: which repository is responsible for which entity? The goal of a query is to get children but a comfortable entry point is the parent.
You also have to have some unit of work (UoW) to collect and "transactionize" the CUD actions of separate repositories.
Why not use the context directly? See this post (it seems I keep quoting it). A context is a class that contains repositories (DbSets or ObjectSets) and keeps track of changes and transactions (UoW). Why wrap this unit of repositories and UoW in yet another layer of repositories and UoW's? That's two layers. And then, you're not going to expose your repositories to client code, because you don't want to expose IQueryables. So you're going to wrap repositories in a service layer that creates, orchestrates and disposes them. That's three layers.
(And repositories should expose IQueryable otherwise they're not composable).
But it is not testable, people say, we want to mock repositories so we can inject these mock repositories into our services for unit testing. That's an illusion. Mocking entity framework behaviour is deceptive and practically impossible. I'm not saying that unit tests are useless. I write unit tests for all kinds of business logic that should work once the objects exist. I just don't write unit tests to test data access code.

Resources