Cannot get record by ID already previously obtained - c#

I've tried this code shown below in another project and it works, so problem is surely not in a method responsible for getting data from database by ID. Unfortunately, I wasn't able figure out what causes the problem.
Simplest test case: I initialize MongoDB class with methods to work with database. Then I load every record stored in database using method LoadRecords() to collection.
// some code
MongoDB db = new MongoDB("People");
List<Person> collection = db.LoadRecords<Person>("Person");
//some code
public List<T> LoadRecords<T>(string table)
{
var collection = db.GetCollection<T>(table);
return collection.Find(new BsonDocument()).ToList();
}
That works fine, it gives me collection of every document in table. Now I try to find only one element with, for example, ID of first element from that collection using method LoadRecordById().
// some code
Person oneRecord = db.LoadRecordById<Person>("Person", people[0].Id);
// some code
public T LoadRecordById<T>(string table, Guid id)
{
var collection = db.GetCollection<T>(table);
var filter = Builders<T>.Filter.Eq("ID", id);
return collection.Find(filter).First();
}
My Person class is also very simple, only includes a few properties and ID:
[BsonIgnoreExtraElements]
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
[BsonId]
public Guid Id { get; set; }
}
It really makes no sense to me, first it gives me every record stored in database (its FirstName, LastName, ID) and then when I want to search one record with the exactly same ID it gave me before, it crashes.
Result is an unhandled exception of type 'System.InvalidOperationException' occurred in System.Core.dll
Additional information: Sequence contains no elements

Related

Incorrect data reader EF 6 with data annotations MVC

the problem im having is related to the datareader being incompatible to the model I specified, i thought that by adding the Column name it would work, but it doesnt. Here is the code related to the problem at hand.
Model:
[Table("IPs")]
public class Ip : BaseEntity
{
[Key]
public int Id { get; set; }
[Column("IdItem")]
public int? ItemId { get; set; }
[Column("EnUso")]
public string En_Uso { get; set; }
//more properties...
}
The method that is failing works with generics, the ModelClass would be Ip here:
var table = TableName<ModelClass>();
var primaryKey = PrimaryKeyProperty<ModelClass>().Name; //this one uses reflection to see which one get the Key
var query = String.Format(FindQuery(), table, primaryKey, id);
// The resulting query is this for example: Select * from IPs WHERE id = 1
var result = DBSet().SqlQuery(query); //here it fails
return result.FirstOrDefault();
The error im getting:
the data reader is incompatible with the specified
'Inventario.Models.Ip'. A member of the type, 'ItemId', does not have
a corresponding column in the data reader with the same name.
If i change the column name to IdItem, then it breaks on "En_Uso", while changing some of the properties wouldnt be a problem
I would like to fix it if possible in a way with data annotations or something of the sort that doesnt requiere to use the exact table names, but I already specified the column name i dont know what else.
to do

Automatically update ID value for records retrieved

So, consider the following class:
public class Solution
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
And the following methods
public void DoSomething()
{
new Item originalItem = new Item();
item.Name = "Test";
InserIntoDb(originalItem);
Assert.True(item.Id != 0);
}
public void InserIntoDb(Item item)
{
context.Item.Add(item);
context.SaveChanges();
}
On this case, after insert into the db, EF automatically updates the originalItem with its auto generated ID value.
My problem starts when I add some logic to only add to the db if the Name doesn't exist. If exists the Insert doesn't happen therefore the ID property is not populated.
My question is: Is there a way to make the context.Item (when retrieving all) to automatically update the ID of the originalItem value without having to change the InsertIntoDb method to return an Item and consequently having to add a line like
originalItem = InsertIntoDb(originalItem);
Thanks, in advance
Nope, there is no built in way to update the item with the id from the db if it exists.
You will have to implement a custom approach to make this work. If this is only a small or single amount of items at once, then just attempt to get the named item from the database prior to entry and then for the one(s) not present insert.
If you are doing this for a large batch of items (more than 5000), consider using a stored procedure to do this work on the db server side for query performance.

Automapper: Mapper.Map does not work in every build

I have very strange issue with AutoMapper
In my windows service, When I create mapping from IDataReader to List object, it works for very first time only when i run the service.
As soon as I stop the service and run it again, AutoMapper cannot able to Map. Below is my code:
Property Class
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Number { get; set; }
}
Actual Implementation
var employeeData = DataHelper.ExecuteReader("Select Id, Name, Number from dbo.Employee");
var employees = new List<Employee>();
employees = employeeData.MapToList<List<Employee>>();
Generic Extension Method
public static T MapToList<T>(this DataTable reader) where T : class
{
Mapper.CreateMap<IDataReader, T>();
// Mapper.AssertConfigurationIsValid();
return Mapper.Map<IDataReader, T>(reader.CreateDataReader());
}
Apart from this, I have noticed that when I clean solution and run service again it starts working.
I am not able to Identify why it behaves like this.
Given your table's fields have the same name as Employee's properties, you should be able to do like this (without any explicit mapping config):
var employeeData = DataHelper.ExecuteReader("Select Id, Name, Number from dbo.Employee");
var employees = AutoMapper.Mapper.DynamicMap<IDataReader, List<Employee>>(employeeData.CreateDataReader());

Updating related data using MVC 4 and Entity Framework?

So, I have a problem in save data which contains related entities, when I save it a new relation blank is created.
Exemple:
Entities:
public class Project
{
public int Id { get; set; }
public int Code{ get; set; }
public string Description{ get; set; }
public virtual Client Client { get; set; }
}
public class Client
{
public int Id { get; set; }
public int Code { get; set; }
public string Name { get; set; }
}
The Controller GET:
public ActionResult Create()
{
PopulateDropDownClienteList(String.Empty); //Returns to ViewBag to create a combobox .in view
return View();
}
The View:
#Html.DropDownListFor(m => m.Client.Id, new SelectList(ViewBag.Client_Id, "Id", "Name"), new { Name = "Client.Id" });
The Controller POST:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(string command, Project project)
{
try
{
if (ModelState.IsValid)
{
projectRepository = new ProjeRepository();
Project pro = projectRepository.ReturnByCode(project.Code);
if (pro == null)
projectRepository.Save(project);
else
projectRepository.Update(project);
PopulateDropDownClienteList(String.Empty);
Return View();
}
else
{
return View(project);
}
}
catch (Exception ex)
{
return View();
}
}
So when I save the data, the client is not associated with the project. just creating a new blank Client.
You Project Save code is not updating the entity, it is ADDING a new one all the time.
You should have update logic similar to following grounds -
To Add new FK Entry and associate it with parent record -
var entity = entities.Students.Where(p => p.Id == "2").First();
entity.StudentContact = new StudentContact() { Contact = "xyz", Id = "2" };
entities.Students.Attach(entity);
var entry = entities.Entry(entity);
// other changed properties
entities.SaveChanges();
To update a FK record with new details -
var entity = entities.Students.FirstOrDefault();
entity.StudentContact.Contact = "ABC";
entities.Students.Attach(entity);
var entry = entities.Entry(entity);
entry.Property(e => e.StudentContact.Contact).IsModified = true;
// other changed properties
entities.SaveChanges();
The above code, I have a Student records which has FK relationship with StudentContacts. I updated Contact information of a student and then updated it to database using ATTACH.
You've got a number of issues here, so let me break them down.
First and foremost, do not ever catch Exception (at least without throwing it again). There's two very important things about using try...catch blocks: you should only wrap the code where you're expecting an exception (not nearly your entire method as you've done here), and you should only catch the specific exception you're expecting (not the base type Exception). When you catch Exception, any and every exception that could possibly be generated from your code will be caught, and in this case, simply discarded, which means you really will never know if this code works at all.
Second, you have a fine method that generates a dropdown list of choices, but never store the user's selection anywhere meaningful. To understand why, you need to stop and think about what's happening here. An HTML select element has a string value and a string text or label component. It does not support passing full objects back and forth. I can't see what your PopulateDropDownClienteList method does, but what it should be doing is creating an IEnumerable<SelectListItem>, where each item gets its Text property set to whatever you want displayed and its Value property to the PK of the Client. However, once you have that, you need some property on Project to post back to. Your virtual Client won't work as that needs a full Client instance, which your form will never have. So, you have two choices:
Implement a view model to feed to the view (and accept in the post). In that view model, in addition to all other editable fields, you'll include something like ClientId which will be an int type, and you'll bind this to your drop down list. Once you're in your post method, you map all the posted values to your project instance, and then use the ClientId to look up a client from the database. You then set the resulting client as the value for your Client property and save as usual.
You alter your database a bit. When you just specify a virtual, Entity Framework smartly creates a foreign key and a column to hold that relationship for you behind the scenes. That's great, but in situations like this, where you actually need to access that foreign key column, you're screwed. That way around that is to explicitly define a property to hold that relationship on your model and tell Entity Framework to use that instead of creating its own.
[ForeignKey("Client")]
public int ClientId { get; set; }
public virtual Client Client { get; set; }
With that, you can now directly use ClientId without worrying about filling in Client. You again bind your drop down list to ClientId, but now, you do not need to look up the client explicitly from the database. Entity Framework will just save the ClientId as it should to the database, and then restore the Client based on that when you look up the project again in the future.

Entity Framework 4.3 Attaching an object from a parameter of an Action of a Controller

I have a situation where I have an object that is loaded back from a form to MVC controller via an action. We do not use FormCollection, but the one that use directly the class.
[HttpPost]
public ActionResult AjaxUpdate(Customer customer) { ...
The Customer object contain an object called customer which seem to be updated but when using SaveDatabase() on the context simply doesn't work.
To make it works I had to use in the action:
myDbContext.Customers.Attach(customer)
//...Code here that set to the customer.SubObject a real object from the database so I am sure that the SubObject contain an id which is valid and the datacontext is aware of it...
myDbContext.Entry(customer).State = EntityState.Modified;
Still, I had an exception concerning the "Store update, insert, or delete statement affected an unexpected number of rows (0)" that I were able to remove by using:
Database.ObjectContext().Refresh(RefreshMode.ClientWins,customer);
So, to warp up my question, why do I have to Attach + change the state + call Refresh. Isn't there a better way to update an object that contain object that are referenced in an other table. I am using Code first Entity Framework (Poco object). Also, I do not like to use Refresh since it's hidden from my Databasecontext.
I've made a console test project with EF 4.3.1. The code is my guess what you mean with the commented line and your comments below the question (but my guess is probably wrong because the program doesn't reproduce your error):
You can copy the code into program.cs and add a reference to EF 4.3.1:
using System.Data;
using System.Data.Entity;
namespace EFUpdateTest
{
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public int SubObjectId { get; set; }
public SubObject SubObject { get; set; }
}
public class SubObject
{
public int Id { get; set; }
public string Something { get; set; }
}
public class CustomerContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<SubObject> SubObjects { get; set; }
}
class Program
{
static void Main(string[] args)
{
int customerId = 0;
int subObject1Id = 0;
int subObject2Id = 0;
using (var ctx = new CustomerContext())
{
// Create customer with subobject
var customer = new Customer { Name = "John" };
var subObject = new SubObject { Something = "SubObject 1" };
customer.SubObject = subObject;
ctx.Customers.Add(customer);
// Create a second subobject, not related to any customer
var subObject2 = new SubObject { Something = "SubObject 2" };
ctx.SubObjects.Add(subObject2);
ctx.SaveChanges();
customerId = customer.Id;
subObject1Id = subObject.Id;
subObject2Id = subObject2.Id;
}
// New context, simulate detached scenario -> MVC action
using (var ctx = new CustomerContext())
{
// Changed customer name
var customer = new Customer { Id = customerId, Name = "Jim" };
ctx.Customers.Attach(customer);
// Changed reference to another subobject
var subObject2 = ctx.SubObjects.Find(subObject2Id);
customer.SubObject = subObject2;
ctx.Entry(customer).State = EntityState.Modified;
ctx.SaveChanges();
// No exception here.
}
}
}
}
This works without exception. The question is: What is different in your code which could cause the error?
Edit
To your comment that you don't have a foreign key property SubObjectId in the customer class: If I remove the property in the example program above I can reproduce the error.
The solution is to load the original subobject from the database before you change the relationship:
// Changed customer name
var customer = new Customer { Id = customerId, Name = "Jim" };
ctx.Customers.Attach(customer);
// Load original SubObject from database
ctx.Entry(customer).Reference(c => c.SubObject).Load();
// Changed reference to another subobject
var subObject2 = ctx.SubObjects.Find(subObject2Id);
customer.SubObject = subObject2;
ctx.Entry(customer).State = EntityState.Modified;
ctx.SaveChanges();
// No exception here.
Without a foreign key property you have an Independent Association which requires that the object including all references must represent the state in the database before you change it. If you don't set the reference of SubObject in customer EF assumes that the original state in the database is that customer does not refer to any subobject. The generated SQL for the UPDATE statement contains a WHERE clause like this:
WHERE [Customers].[Id] = 1 AND [Customers].[SubObject_Id] IS NULL
If the customer has a subobject in the DB [SubObject_Id] is not NULL, the condition is not fulfilled and the UPDATE does not happen (or happens for the "unexpected number of rows 0").
The problem does not occur if you have a foreign key property (Foreign Key Association): The WHERE clause in this case is only:
WHERE [Customers].[Id] = 1
So, it doesn't matter what's the original value of SubObject and of SubObjectId. You can leave the values null and the UPDATE works nonetheless.
Hence, the alternative solution to loading the original subobject is to introduce a foreign key property in Customer:
public int SubObjectId { get; set; }
Or, in case the relationship is not required:
public int? SubObjectId { get; set; }

Categories