I'm trying to use a Kendo UI grid in MVC and remote data. I want to only grab and display data from the DbSet, onload, where one of the fields, "Status", equals '1'. I thought this should be able to be accomplished in the controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Read([DataSourceRequest] DataSourceRequest request)
{
using (var db = new MyData(false))
{
var data = db.Training.Where(d => d.Status == '1').Select(d => new Training {
Id = d.Id,
Name = d.Name,
Status = d.Status
}).ToDataSourceResult(request);
return Json(data, JsonRequestBehavior.AllowGet);
}
}
The above code gives me the error that "The entity or complex type 'Training' cannot be constructed in a LINQ to Entities query". Any suggestions on how to rewrite the Linq statement so it'll work, or maybe a way to do it within the grid to suppress any that do not have a Status of '1'?
Your code is trying to project to a mapped entity which is not allowed. Additionally it's redundant to do that as you already have your entities. Remember that .Select() is for mapping one type to another but the .Where() method is already returning a list of your entities (Training).
Remove the .Select() and the query should work:
var data = db.Training.Where(d => d.Status == '1').ToDataSourceResult(request);
Related
The model of my project is database first, and uses remote access to database on another server.
I need to use raw SQL query because my query is very complex and I feel more comfortable in SQl not LINQ.
This is how I do:
string query = "select * from Inquiry_TBL where ...";
using (educationEntities db = new educationEntities())
{
var list = db.Database.SqlQuery<Inquiry_TBL>(query);
ViewData["total"] = list.Count();
}
The problem is sometimes I get the query result within a second, sometimes it just keep loading for a long time and gives me an error that 'Calling 'Read' when the data reader is closed is not a valid operation.'
Why is that? Is there something wrong with my code, or because I'm using remote access to another server? Will switching to local server solve the problem?
The Entity Framework Code First API includes methods that enable you to pass SQL commands directly to the database. You have the following options:
• Use the DbSet.SqlQuery method for queries that return entity types. The returned objects must be of the type expected by the DbSet object, and they are automatically tracked by the database context unless you turn tracking off. (See the following section about the AsNoTracking method.)
• Use the Database.SqlQuery method for queries that return types that aren't entities. The returned data isn't tracked by the database context, even if you use this method to retrieve entity types.
• Use the Database.ExecuteSqlCommand for non-query commands.
Calling a Query that Returns Entities:
public async Task<ActionResult> Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
// Commenting out original code to show how to use a raw SQL query.
//Department department = await db.Departments.FindAsync(id);
// Create and execute raw SQL query.
string query = "SELECT * FROM Department WHERE DepartmentID = #p0";
Department department = await db.Departments.SqlQuery(query, id).SingleOrDefaultAsync();
if (department == null)
{
return HttpNotFound();
}
return View(department);
}
Calling a Query that Returns Other Types of Objects:
public ActionResult About()
{
//Commenting out LINQ to show how to do the same thing in SQL.
//IQueryable<EnrollmentDateGroup> = from student in db.Students
// group student by student.EnrollmentDate into dateGroup
// select new EnrollmentDateGroup()
// {
// EnrollmentDate = dateGroup.Key,
// StudentCount = dateGroup.Count()
// };
// SQL version of the above LINQ code.
string query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
+ "FROM Person "
+ "WHERE Discriminator = 'Student' "
+ "GROUP BY EnrollmentDate";
IEnumerable<EnrollmentDateGroup> data = db.Database.SqlQuery<EnrollmentDateGroup>(query);
return View(data.ToList());
}
Calling an Update Query:
[HttpPost]
public ActionResult UpdateCourseCredits(int? credit)
{
if (credit != null)
{
ViewBag.RowsAffected = db.Database.ExecuteSqlCommand(
"UPDATE Course SET Credits = Credits * {0}", credit);
}
return View();
}
For more information have a look at Advanced Entity Framework 6 Scenarios for an MVC 5 Web Application (12 of 12).
I have two sub-types of a super-type "Entity", namely "Household" and "Involved Body".
I've modeled them as shown below in my database and they were auto-generated to the EF Model (again shown below).
database
edmx model
Using the default scaffolding for MVC I am able to add a new Household without any problems. However, when I try to add a new Involved Body I hit an error when it tries to add the Entity Type.
There only relevant (as far as I can tell) difference between the two sub-types is that the EntityType for a Household is hard-coded as "Household" whereas the EntityType for an Involved Body can be any EntityType except "Household" - this is selected from a list by the user.
The Create Action on the HTTP POST for the Involved Body throws an error relating to the foreign key between tEntity and tEntityType with the tEntityType being null. Code as follows:
[HttpPost]
public ActionResult Create([Bind(Exclude = "entityID")]tEntity tentity
, tInvolvedBody tinvolvedbody
, tAddress taddress
, tAddressEntity taddressentity
//, tEntityType tentitytype
, int entityTypeID
)
{
#region entity type
//find entity type from id
var tentitytype = db.tEntityTypes.Find(entityTypeID);
#endregion
#region address
//assume start date of involved body not needed for reporting
taddressentity.startDate = DateTime.Now.Date;
#endregion
if (ModelState.IsValid)
{
db.tEntities.Add(tentity);
db.tInvolvedBodies.Add(tinvolvedbody);
db.tAddresses.Add(taddress);
db.tAddressEntities.Add(taddressentity);
db.SaveChanges();
return RedirectToAction("Index");
}
//recreate viewbag for entityType dropdown
var q = (
from e in db.tEntityTypes
where e.entityType != "Household"
select e
);
ViewBag.entityTypeID = new SelectList(q, "entityTypeID", "entityType");
return View(tinvolvedbody);
}
I've tried adding the tEntityType to the parameters list for the create but this results in the ModelState.IsValid returning false due to the entityType being null on all the objects.
I've also tried actively linking the entity type to each of the other objects using:
tentity.tEntityType = tentitytype;
tinvolvedbody.tEntity.tEntityType = tentitytype;
taddressentity.tEntity.tEntityType = tentitytype;
The above ends up working but it creates a new Entity for each of the other objects i.e. I get three new rows in my tEntity table, one is the Entity, one links to tInvolvedBody and one links to tAddressEntities. This makes no sense...
How can I insert a new InvolvedBody that creates an Entity, picks up the Entity Type and then links to the AddressEntity junction table?
Finally worked through this. Not sure if the answer is 'perfect' from a developer perspective but it works.
After intense debugging I realised that the navigation properties for the involved body and address entity were both looking for an entitytypeID which I had assumed would be provided by the entity object.
If I passed these in directly with the code shown:
tinvolvedbody.tEntity.tEntityType = tentitytype;
taddressentity.tEntity.tEntityType = tentitytype;
...I ended up with three new entitites and no relational data existing between all of {entity, involved body, address}
The code that works removes the explicit addition of a new entity and relies on EF to create an entity from the Involved Body. I then used the newly created entityID to map the address via addressentity as follows:
[HttpPost]
public ActionResult Create([Bind(Exclude="entityID")]tEntity tentity
,tInvolvedBody tinvolvedbody
,tAddress taddress
,tAddressEntity taddressentity
,int entityTypeID
)
{
#region entity type
var t =
(
from e in db.tEntityTypes
where (e.entityTypeID == entityTypeID)
select e
);
tinvolvedbody.tEntity.tEntityType = t.First();
#endregion
#region address
//assume start date of involved body not needed for reporting
taddressentity.startDate = DateTime.Now.Date;
#endregion
if (ModelState.IsValid)
{
db.tInvolvedBodies.Add(tinvolvedbody);
db.tAddresses.Add(taddress);
taddressentity.tEntity = db.tEntities.Find(tinvolvedbody.bodyID);
db.tAddressEntities.Add(taddressentity);
db.SaveChanges();
return RedirectToAction("Index");
}
Have you tried setting the typeID specifically? Also, from what I gathered from your model, the taddress is a child of taddressentity? As such, should it not be inserted first in order for the foreign key?
if (ModelState.IsValid)
{
tentity.entityTypeID = entityTypeID;
db.tEntities.Add(tentity);
tinvolvebody.bodyID= tentity.entityID
db.tInvolvedBodies.Add(tinvolvedbody);
taddressentity.entityID = tentity.entityID;
db.tAddressEntities.Add(taddressentity);
taddress.UPRN = taddressentity.UPRN;
db.tAddresses.Add(taddress);
db.SaveChanges();
return RedirectToAction("Index");
}
When I update my model I get an error on a child relation which I also try to update.
My model, say Order has a releationship with OrderItem. In my view I have the details of the order together with an editortemplate for the orderitems. When I update the data the link to Order is null but the orderid is filled, so it should be able to link it, TryUpdateModel returns true, the save however fails with:
InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.]
My update method:
public ActionResult ChangeOrder(Order model)
{
var order = this.orderRepository.GetOrder(model.OrderId);
if (ModelState.IsValid)
{
var success = this.TryUpdateModel(order);
}
this.orderRepository.Save();
return this.View(order);
}
I tried all solutions I saw on SO and other sources, none succeeded.
I use .Net MVC 3, EF 4.3.1 together with DBContext.
There are a number of code smells here, which I'll try to be elegant with when correcting :)
I can only assume that "Order" is your EF entity? If so, I would highly recommend keeping it separate from the view by creating a view model for your form and copying the data in to it. Your view model should really only contain properties that your form will be using or manipulating.
I also presume orderRepository.GetOrder() is a data layer call that retrieves an order from a data store?
You are also declaring potentially unused variables. "var order =" will be loaded even if your model is invalid, and "var success =" is never used.
TryUpdateModel and UpdateModel aren't very robust for real-world programming. I'm not entirely convinced they should be there at all, if I'm honest. I generally use a more abstracted approach, such as the service / factory pattern. It's more work, but gives you a lot more control.
In your case, I would recommend the following pattern. There's minimal abstraction, but it still gives you more control than using TryUpdateModel / UpdateModel:
public ActionResult ChangeOrder(OrderViewModel model) {
if(ModelState.IsValid) {
// Retrieve original order
var order = orderRepository.GetOrder(model.OrderId);
// Update primitive properties
order.Property1 = model.Property1;
order.Property2 = model.Property2;
order.Property3 = model.Property3;
order.Property4 = model.Property4;
// Update collections manually
order.Collection1 = model.Collection1.Select(x => new Collection1Item {
Prop1 = x.Prop1,
Prop2 = x.Prop2
});
try {
// Save to repository
orderRepository.SaveOrder(order);
} catch (Exception ex) {
ModelState.AddModelError("", ex.Message);
return View(model);
}
return RedirectToAction("SuccessAction");
}
return View(model);
}
Not ideal, but it should serve you a bit better...
I refer you to this post, which is similar.
I assume that the user can perform the following actions in your view:
Modify order (header) data
Delete an existing order item
Modify order item data
Add a new order item
To do a correct update of the changed object graph (order + list of order items) you need to deal with all four cases. TryUpdateModel won't be able to perform a correct update of the object graph in the database.
I write the following code directly using a context. You can abstract the use of the context away into your repository. Make sure that you use the same context instance in every repository that is involved in the following code.
public ActionResult ChangeOrder(Order model)
{
if (ModelState.IsValid)
{
// load the order from DB INCLUDING the current order items in the DB
var orderInDB = context.Orders.Include(o => o.OrderItems)
.Single(o => o.OrderId == model.OrderId);
// (1) Update modified order header properties
context.Entry(orderInDB).CurrentValues.SetValues(model);
// (2) Delete the order items from the DB
// that have been removed in the view
foreach (var item in orderInDB.OrderItems.ToList())
{
if (!model.OrderItems.Any(oi => oi.OrderItemId == item.OrderItemId))
context.OrderItems.Remove(item);
// Omitting this call "Remove from context/DB" causes
// the exception you are having
}
foreach (var item in model.OrderItems)
{
var orderItem = orderInDB.OrderItems
.SingleOrDefault(oi => oi.OrderItemId == item.OrderItemId);
if (orderItem != null)
{
// (3) Existing order item: Update modified item properties
context.Entry(orderItem).CurrentValues.SetValues(item);
}
else
{
// (4) New order item: Add it
orderInDB.OrderItems.Add(item);
}
}
context.SaveChanges();
return RedirectToAction("Index"); // or some other view
}
return View(model);
}
Is there a pre-built ModelBinder I can use with LINQ to get an object from a DataContext and update it on a HTTP post?
For example, currently I have this block of code:
[AcceptVerbs (HttpVerbs.Post)]
public ActionResult Edit (Project project)
{
var projectService = Factory.GetService<IProjectService> ();
project = projectService.GetProject (project.ProjectId);
UpdateModel<Project> (project);
if (!ModelState.IsValid)
return View (project);
project = projectService.SaveProject (project);
return RedirectToAction ("Details", new { id = project.ProjectId });
}
(IProjectService wraps up calls to a LINQ data context)
In order to actually perform the update to the database via the LINQ data context, I need to get the project instance again and then update that instance.
Any attempt to simply save the project instance without first getting it from the data context results in nothing being written back to the database - I'm assuming because the LINQ data context knows nothing of the object it doesn't do anything with it.
Using the Attach method on the Projects table class doesn't work either btw, it throws an exception.
You should look at the implementation in Mike Hadlow's (new BSD) SutekiShop.
In there you will find a DataBindAttribute and BindUsingAttribute which, if I understand correctly, do exactly what you want to do. Notice how the DataBindAttribute.Fetch property is used to rebind the incoming data, or not, (from an HttpPost) to a LINQ entity.
I followed this pattern for one of my projects using ASP.NET MVC and LINQ-To-SQL. It works beautifully.
Here's the source: http://www.google.com/codesearch?hl=en&lr=&q=DataBind+package%3Ahttp%3A%2F%2Fsutekishop.googlecode.com&sbtn=Search
I think the project you pass in to the method is the one you want to perform UpdateModel with isn't it?
Otherwise you are trying to update with pre-existing values not new ones.
Just a thought,
Dan
Code cut out below
[AcceptVerbs (HttpVerbs.Post)]
public ActionResult Edit (Project project)
UpdateModel<Project> (project);
if (!ModelState.IsValid)
return View (project);
var projectService = Factory.GetService<IProjectService> ();
project = projectService.SaveProject (project);
return RedirectToAction ("Details", new { id = project.ProjectId });
}
You need to retrieve the original project as you do then to update it with the properties that have changed in project to update then to submit the update request.
EDIT
Try this code I found:
public static void CloneProperties(this object origin, ref object destination)
{
if (destination == null) throw new ArgumentNullException("destination", "Destination object must first be instantiated.");
foreach (var destinationProperty in destination.GetType().GetProperties())
{
if (origin != null && destinationProperty.CanWrite)
{
origin.GetType().GetProperties().Where(x => x.CanRead && (x.Name == destinationProperty.Name && x.PropertyType == destinationProperty.PropertyType)) .ToList() .ForEach(x => destinationProperty.SetValue(destination, x.GetValue(origin, null), null));
}
}
}
I've got a field which its type is byte[]. This field will hold my entity's RecordVersion property (timestamp in the database). How do I keep this field so that when I save my entity it is available?
I've tried two different things and haven't succeeded so far:
This renders "System.Byte[]":
<%= Html.Hidden("RecordVersion", Model.RecordVersion.ToString()) %>
This throws a ModelStateError where the type couldn't be converted:
ViewData["RecordVersion"] = entity.RecordVersion
Apparently the default MVC's mechanism that does the bind/unbind doesn't like much byte[] fields .....
You need to make a modelbinder and register it.
This article shows how to use a timestamp from a linq database in a hidden field much like what you are doing.
ModelBinders.Binders.Add(typeof(Binary), new LinqBinaryModelBinder());
In global.asax to register it.
That LinqBinaryModelBinder is in the futures assembly. If you want to user byte[] you'll have to write one yourself.
Have you tried.
<%= Html.Hidden("RecordVersion", System.Text.Encoding.Unicode.GetString(Model.RecordVersion)) %>
I wouldn't put the timestamp on the form. If you want to keep the object around I'd cache it server side and retrieve it from the cache using the id. Otherwise, you can re-retrieve the object from the database and apply the changes from your form data. The latter is what I do, using TryUpdateModel.
public ActionResult Update( int id )
{
using (var context = new DataContext())
{
var model = context.Models.Where( m => m.ID == id ).Single();
if (TryUpdateModel( model ))
{
...
context.SubmitChanges(); // wrapped in try/catch
...
}
else
{
...
}
}
return RedirectToAction( "Show", new { id = id } );
}