EF 5.0 Code First Winforms Combobox Data Binding - entity-framework

I'm trying to bind some lookup tables to comboboxes on winforms. I've built the POCCO classes and generated the database. I've added the data source to my app. I drop the source tables onto the comboboxes in the designer and the binding gets set up fine for each. I've populated the tables in the db with test data.
Here's where I need help. With datasets I would simply do a tableadapter fill on the form Load event to get the data. With EF I must have to do something to load the data. Perhaps a query? Something else? I bleieve everything is set up correctly. Just need the final step to get it to load and work. Thanks.

There are many ways how to communicate with your database. I understood, you are probably using EF Code First approach. Imagine this context:
public class WinFormContext : DbContext
{
public DbSet<Car> Cars { get; set; }
}
public class Car
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
You are gonna add some record first. You create simple form with textboxs and one button.
private void button1_Click(object sender, EventArgs e)
{
using (var WinFormContext = new WinFormContext())
{
Car car = new Car { Name = textBox1.Text };
WinFormContext.Cars.Add(car);
WinFormContext.SaveChanges();
}
}
It is simple solution, how to add new record after onClick event. Now you want to show all record in Grid View. One of the possible solution is fill the controll manually:
BindingSource bindingSource = new BindingSource();
private void Form2_Load(object sender, EventArgs e)
{
var WinFormContext = new WinFormContext();
bindingSource.DataSource = WinFormContext.Cars.ToList();
dataGridView1.DataSource = bindingSource;
dataGridView1.AutoGenerateColumns = true;
}
But there is more ways how to do it. I recommend you take a look on "data binding in win forms" on Google...

Related

Can't create related entity in ASP.NET Core with EF Core

I have a problem creating a related entity in Entity Framework Core 2.0. I've just created the solution, consisting of an Asp.Net Core backend project, and a UWP project to act as client. Both solutions share model. The two models are:
public class UnitOfWork {
public int UnitOfWorkId { get; set; }
public string Name { get; set; }
public Human Human { get; set; }
}
public class Human {
public int HumanId { get; set; }
public string Name { get; set; }
public List<UnitOfWork> WorkDone { get; set; }
}
As you can see, model is very simple. One human has many units of work. By the way, the backend is connected to an Azure SQL database. I've seen the migration classes, and the database schema looks good to me.
The problem I have is when I want to create a unit of work referencing an existing human, using HTTP. The controller is fairly simple:
[HttpPost]
public UnitOfWork Post([FromBody] UnitOfWork unitOfWork) {
using (var db = new DatabaseContext()) {
db.UnitsOfWork.Add(unitOfWork);
var count = db.SaveChanges();
Console.WriteLine("{0} records saved to database", count);
}
return unitOfWork;
}
Again, nothing fancy here.
How can I create an unit of work, and assign it to an existing human? If I try it with an existing human, in this way
var humans = await Api.GetHumans();
var firstHuman = humans.First();
var unitOfWorkToCreate = new UnitOfWork() {
Name = TbInput.Text,
Human = firstHuman,
};
I get this error:
Cannot insert explicit value for identity column in table 'Humans' when IDENTITY_INSERT is set to OFF
I feel that setting IDENTITY_INSERT to ON will solve my problem, but this is not what I want to do. In the client, I'll select an existing human, write down a name for the unit of work, and create the latter. Is this the correct way to proceed?
EDIT: Following #Ivan Stoev answer, I've updated the UnitOfWork controller to attach unitofwork.Human. This led to
Newtonsoft.Json.JsonSerializationException: 'Unexpected end when deserializing array. Path 'human.workDone', line 1, position 86.'
Investigating - seen here - EFCore expects to create collections (like human.WorkDone) in the constructor, so I did it, and no more nulls deserializing. However, now I have a self-referencing loop:
Newtonsoft.Json.JsonSerializationException: Self referencing loop detected with type 'PlainWorkTracker.Models.UnitOfWork'. Path 'human.workDone'.
Any ideas? Thanks!
The operation in question is falling into Saving Disconnected Entities category.
Add methods marks all entities in the graph which are not currently tracked as new (Added) and then SaveChanges will try to insert them in the database.
You need a way to tell EF that unitOfWork.Human is an existing entity. The simplest way to achieve that is to Attach it (which will mark it as Unchanged, i.e. existing) to the context before calling Add:
db.Attach(unitOfWork.Human);
db.Add(unitOfWork);
// ...

Using Entity Framework to create a custom model that is not a model of any table in a database

I am using the .NetCore Entity Framework for the first time and want to know if the it is possible to generate a custom model.
When setting up the app, EF created all the models from the database. That is fine and expected.
However, I now created a new controller that returns data that is the result of a complicated linq query.
All my other controllers return a model like this:
return View(characterList);
where characterList is an actual model of a database table.
But how would I create a brand new custom model that does not represent any table in the database?
Thanks!
You would first simply create the model you want to have in your code.
For example:
Public class NewModel {
Public String Test {get; set;}
}
Then you can use your context and the power of linq/select to query in your new model.
Something like this:
List<NewModel> list = dbContext.Set<OldModel>().Where(...).Select<NewModel>(x=> new NewModel(){ Test = x.OldTestString }).ToList()
And so you get a list of the new model. You could e.g. include other tables and join them in the query to make it more complicated. But this example should give you a starting point.
If the model you are explaining is supposed to be used only for the views consider creating a ViewModel which is basically a class that contains only the properties needed for the view usually without any logic or only with a logic immediatelly necessary for displaying in a view.
For example, you'd create a new class, let's say CharacterVM
public class CharacterVM
{
public string Name{ get; set; }
public string CharacterType {get; set; }
public bool Invincible{ get; set; }
}
In your view you'd use CharacterVM which has all the properties exposed in the CharacterVM class
#model CharacterVM
The most important step is remapping the properties from your database model (let's say it is called Character) where all you have to do in that case is to remap the properties of the Character to the properties of the new instance of CharacterVM you'd pass to the view.
public IActionResult Index(int idCharacter)
{
var character = db.Characters.SingleOrDefault(c => c.idCharacter == idCharacter);
var characterVM = new CharacterVM()
{
Name = character.Name,
CharacterType = character.Type.Name,
Invincibility = false
};
return View(characterVM);
}

Having an ICollection with references only instead of creatiing copies/clones

I have a Survey that contains questions and also which users can/will participate in the survey.
like so
public virtual ICollection<User> ParticipatingUsers { get; set; }
public virtual ICollection<Question> SpecificQuestions { get; set; }
However, due to the ajaxy solution I create the questions first and then simply send in the ID of my created question with the survey data. So all I need to do is change the sortingIndex of the question and then add a reference to it in my Survey.
When it comes to users they belong to a Company entity and I only want to reference them from the survey not own them.
But currently I get all the id's for questions and users in my action method (.net mvc) and so currently I load all questions and users and attach them to my survey entity before sending the survey to the repository.
But when my Repository calls Add on dbset it clones the user and question data instead of simply referencing existing data.
I am lost, I have solved this exact problem for a normal navigation property by adding [Foreignkey] but i don't know how that would work with ICollection
For completeness
Here is my action method recieving the data
[HttpPost]
[FlexAuthorize(Roles = "RootAdmin")]
public ActionResult SaveSurvey(EditSurveyViewModel editModel)
{
if (!ModelState.IsValid)
{
//We dont bother to send this in so we need to fetch the list again
editModel.CompanyList = _companyRepository.GetAll();
List<string> deletionList = new List<string>();
//We clear out all questions from the state as we have custom logic to rerender them with the correct values
foreach (var modelstateItem in ModelState)
{
if (modelstateItem.Key.StartsWith("Questions"))
{
deletionList.Add(modelstateItem.Key);
}
}
foreach (string key in deletionList)
{
ModelState.Remove(key);
}
return View("EditSurvey", editModel);
}
List<Question> questionlist = new List<Question>();
int sort = 1;
Question q;
//We have questions sent in from the ui/client
if (editModel.Questions != null)
{
//Go trough each questions sent in
foreach (var question in editModel.Questions)
{
//if it's a page break, just assign our new question the sent in one and set sort index
if (question.IsPageBreak)
{
q = question;
q.SortIndex = sort;
}
else
{
//It's a question and all questions are already created with ajax from the client
//So we simply find the question and then set sort index and tie it to our survey
q = _questionRepository.GetById(question.Id);
q.SortIndex = sort;
}
questionlist.Add(q);
sort++;
}
}
//assign the new sorted questions to our Survey
editModel.Item.SpecificQuestions = questionlist;
List<User> userlist = new List<User>();
foreach (int id in editModel.SelectedUsers)
{
userlist.Add(_userRepository.GetById(id));
}
editModel.Item.ParticipatingUsers = userlist.ToList();
_surveyRepository.SaveSurveyBindAndSortQuestionsLinkUsers(editModel.Item);
return RedirectToAction("Index");
}
Here is the viewmodel the method gets sent in
public class EditSurveyViewModel
{
public Survey Item { get; set; }
public IEnumerable<Question> Questions { get; set; }
public bool FullyEditable { get; set; }
public IEnumerable<Company> CompanyList { get; set; }
public IEnumerable<int> SelectedUsers { get; set; }
}
Finally here is the repo method (so far i only implemented insert, not update)
public void SaveSurveyBindAndSortQuestionsLinkUsers(Survey item)
{
if (item.Id == 0)
{
Add(item);
}
ActiveContext.SaveChanges();
}
Update/Edit
Moho: You are of course correct, I think to my shame I was testing some things and forgot to reset the method before pasting it in here.
I have updated the action method above.
Slauma: Sorry for lack of details, here comes more.
All my repositories look like this
public class EFSurveyRepository : Repository<Survey>, ISurveyRepository
So they inherit a generic repository and implement an interface
The generic repository (the part we use in code above, looks like this)
public abstract class Repository<T> : IRepository<T> where T : class
{
public EFDbContext ActiveContext { get; private set; }
private readonly IDbSet<T> dbset;
public Repository()
{
this.ActiveContext = new EFDbContext("SurveyConnection");
dbset = ActiveContext.Set<T>();
}
public virtual void Add(T entity)
{
dbset.Add(entity);
}
public virtual T GetById(int id)
{
return dbset.Find(id);
}
I have noticed in the database that my User table (for User entity) now contains a Survey_Id field which i do not want it to have. I want a many-to-many where many surveys can link to many users (the same users) but the users should entity-wise still only belong to a Department in a Company.
Also, right now when I run the code (after I corrected my action method) I get the following error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
No InnerException, only that when i try to add the new survey.
The problem is that you are using separate contexts per repository:
public Repository()
{
this.ActiveContext = new EFDbContext("SurveyConnection");
//...
}
In your POST action you have four repositories in place: _companyRepository, _questionRepository, _userRepository and _surveyRepository. It means you are working with four different contexts, i.e. you load data from different contexts, create relationships between entities that are attached to different contexts and save the data in yet another context.
That's the reason for the entity duplication in the database, for the "multiple instances of IEntityChangeTracker" exception and will be the source for many other problems you might encounter in future.
You must refactor the architecture so that you are using only one and the same context instance ("unit of work") in every repository, for example by injecting it into the constructor instead of creating a new one:
private readonly EFDbContext _activeContext;
private readonly IDbSet<T> _dbset;
public Repository(EFDbContext activeContext)
{
_activeContext = activeContext;
_dbset = activeContext.Set<T>();
}
You build up questionList, set it to editModel.Item.SpecificQuestions, then overwrite the reference by settting that same property to editModel.Questions.ToList(), which is from your view model (i.e.: not loaded via your database context like questionList's question objects) and therefore appears to be new questions to your database context,
editModel.Item.SpecificQuestions = questionlist;
// what is this? why?
editModel.Item.SpecificQuestions = editModel.Questions.ToList();
Edit after question update:
Instead of using questionList and assigning to the questions property of the Survey, simply use the property directly.
Also, do you realize that if you're reusing Question records from the DB for multiple Surveys, you're updating the sort order at the question itself and not simply for that Survey? Each time you save a new survey that reuses questions, other surveys' question ordering will me altered. Looks like you need a relationship entity that will map Questions to Surveys where you can also store the sort order so that each survey can reuse question entities without messing up existing surveys question ordering.

Entity Framework / MVC Remove Item from Collection

What are some ways I can delete an item from a collection? (I am using MVC 4 and EF.)
As an example:
public class Birthday
{
public string Name { get; set; }
public virtual ICollection<Gift> Gifts { get; set; }
}
public class Gift
{
public string Name { get; set; }
public double Price { get; set; }
}
I'm using Editing a variable length list, ASP.NET MVC 2-style to create a dynamic list of Gifts.
The example is shows how to "Delete" a row. This will delete the row from the page and the correct Gifts are sent to the controller.
When I update the Birthday / Gifts everything new is updated properly, but anything deleted is still there.
So my question is what are some preferred ways to remove Gifts?
Two ways I've thought of already:
Get a Birthday from the DB and compare the Gifts removing as needed. I don't love this idea because it seems heavy handed.
Use WebApi / Ajax and delete the Gift from the list and the DB when the user pushes the delete link. I like this better than #1 but does this put too much business logic in the presentation layer?
I'm guessing that other people have had this similar problem and have a clever solution I haven't thought of yet.
Thanks in advance!
Make a Gifts api controller.
Let it have a Delete method accepting an Id of whatever type your Id is.
And do something like this in it:
public class GiftsController: ApiController
{
public void Delete(Guid Id)
{
var context = new MyContext();
var giftToDelete = context.Gifts.FirstOrDefault(g=> g.Id == Id);
if(giftToDelete != null)
{
context.Gifts.Remove(giftToDelete);
context.SaveChanges();
}
}
}
Make sure you make a DELETE request to this api in your JS delete function.
You may also replace the body of this method with some Service.DeleteGift(Id) if you're too concerned about doing things in the right place.
Like this:
public class ValuesController : ApiController
{
private List<string> list = new List<string>{"Item1","Item2","Item3","Item4","Item5"};
// DELETE api/values/5
public List<string> DeleteItem(int id)
{
list.Remove(list.Find((i => i.ToString().Contains(id.ToString()))));
return list;
}
}

MVVM: Delete a CustomerViewModel, but how to get the Customer model inside it?

I have a list of CustomerViewModels in a ComboBox. The selected CustomerViewModel I want to delete and also the Customer wrapped inside it to remove it from the repository.
But how can I access the Customer model inside the CustomerViewModel?
Just a suggestion, make your collection of customerviewmodels an ObserableCollection of CustomerViewModels.
what this buys you is a CollectionChanged Event that you could listen on with a delegate for changes to the collection ie deletion, so from there you could manipulate you model accordingly
http://msdn.microsoft.com/en-us/library/ms653375(VS.85).aspx
perhaps something like
public class CustomersViewModel: ViewModelBase
{
public ObservableCollection<CustomersViewModel> Customers { get; private set; }
public CustomersViewModel()
{
Customers = new ObservableCollection<CustomersViewModel>(GetCustomers());
Customers.CollectionChanged +=
(sender, args) =>
{
if (args.Action == NotifyCollectionChangedAction.Remove)
{
foreach (CustomerViewModel customerViewModel in args.NewItems)
{
DeleteCustomer(customerViewModel.Customer);
}
}
};
}
private void DeleteCustomer(Customer customer)
{
// Call into your repo and delete the customer.
}
private List<CustomersViewModel> GetCustomers()
{
// Call into your model and return customers.
}
... ICommands ect...
}
You might have already access to the Customer inside CustomerViewModel (the VieModel needs to expose the properties of the Customer so the View can databind on them; I usually do it by exposing the Customer or a copy of it directly).
The point is that you should not delete the Customer yourself. That's what the ViewModel is for, to expose an ICommand that deletes the associated Customer. Depending on which MVVM framework you are using, look into DelegateCommand or another equivalent.
Your CustomerViewModel would have a
public ICommand DeleteCommand { get; private set; }
and your View would bind a CommandTarget (probably a Button) to this command. When the command is executed a private method of CustomerViewModel will be run, and you can delete the Customer from there without exposing the deletion mechanism to other parts of the code. For example:
public CustomerViewModel()
{
this.DeleteCommand = new DelegateCommand(this.ExecuteDeleteCommand);
}
private void ExecuteDeleteCommand()
{
// remove the Customer from the ObservableCollection of customers
// and also delete it from the database, or do anything else you want
}