I'm working on a desktop application using C# and EF6.
For some reasons (One would be the complexity of the structure of the models) I've decided to use only on DbContext for the whole project, instead of create and dispose every time I need to add, update, delete or fetch any data.
Let's say I have 2 Models
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
public class CollegeStudent : Student
{
public string Course { get; set; }
}
I have an ObservableCollection in the ViewModel and instantiate it after I add an item to the collection.
I add and Student object to the database in the following way
public void AddStudent()
{
var obj = new Student() { Name = "Mike" };
_context.Set<Student>().Add(obj);
StudentCollection = new ObservableCollection<Student>(_context.Set<Student>().ToList());
}
And when I want to change the type of the Student to the CollegeStudent I use the following piece of code
public void AddCollegeStudent(CollegeStudent obj)
{
var original = _context.Set<Student>().Find(obj.Id);
var obj = new Student()
{
Id = original.Id,
Name = original.Name,
Course = "Some Course",
}
_context.Database.ExecuteSqlCommand("INSERT INTO CollegeStudent (Id, Course) VALUES (obj.Id, '" + obj.Course + "');");
StudentCollection = new ObservableCollection<Student>(_context.Set<Student>().ToList());
}
It perfectly works and insert the CollegeStudent details in the database but when getting the list of students from the database it throws the following
exception:
All objects in the EntitySet 'Students' must have unique primary keys. However, an instance of type 'CollegeStudent' and an instance of type 'Student' both have the same primary key value, 'EntitySet=Students;Id=4'
I've decided to use only on DbContext for the whole project, instead of create and dispose every time I need to add, update, delete or fetch any data.
There's your problem...
This is one reason why you shouldn't you a single DbContext for an entire app - changes to underlying data can make the data in your context invalid. Contexts are meant to be created and disposed with every DB operation. They are lightweight so creating lots of them shouldn't be a big problem.
I realise you are likely trying to keep things as straightforward as possible but it might be worthwhile separating your concerns sooner rather than later.
Assuming this is a XAML UI you could make use of a framework like MVVM Light or Prism
https://mvvmlight.codeplex.com
https://msdn.microsoft.com/en-us/library/ff648465.aspx
If not the basics are you want some kind of mediator (http://www.blackwasp.co.uk/mediator.aspx)
So the idea is you will have some kind of service class that makes a call to save the data, then raises the message/event saying that the data was updated.
You would register a handler for when that event is raises to update the view model accordingly.
Hope this makes sense.
Related
Each time that I want to assign a db-entity to another, I'm creating a new one.
Working with Entity Framework, code first, creating a restfull webapi service disconnected from Angular UI. For each db entity, I also have a Data obj to send result-data to UI.
Lets consider next example
DB ENTITIES:
Client()
{
int Id {get;set;}
string Name {get;set;}
CountriesDropDown Contry { get; set; }
}
CountriesDropDown()
{
int Id {get;set;}
string Description {get;set;}
}
CountriesDropDown is a fixed list at DB, I dont want to create new rows or edit them, I just want to assign them to a Client.
IS THERE ANY WAY TO SET THIS DROPDOWN TABLE AT ENTITY FRAMEWORK AS ALWAYS EntityState.Unchanged??
THEN:
SAVE()
{
.....
.....
if(Client.Contry != null)
{
Client.Contry.Id = ClientData.Contry.Id;
}
else
{ //INSTEAD OF ASSIGN AN EXISTING COUNTRY IT CREATES A DUPLICATE ONE
Client.Contry = new CountriesDropDown();
Client.Contry.Id = ClientData.Contry.Id;
Client.Contry.Description = ClientData.Contry.Description;
}
}
There are two ways round this.
First in your Client class have a CountryId field and link it to your Country object via a ForeignKeyAttribute
Then just set the Id attribute
Client.CountryId = clientData.CountryId
In querying the data you can still access the navigation property
Secondly Retrieve the existing entity via the db context and attach it
Client.Country = dbContext.Countries.Find(clientData.CountryId)
I'm trying to develop a messeging system to my mvc application using mvc 5. I have tables called Event, EventUser, EventObject. Each of those tables have following;
Event
ID
CreatedBy
StartTime
IsShared
Budget
EventUser
EventID
UserID
IsAccepted
EventObject
EventID
ObjectID
in my messageController i have the index method which receive the parameter of the user id.i need to display every event that user has invited using this method..
namespace MvcApp.Controllers
{
public class MessageController : Controller
{
private EPlannerDatabaseEntities db = new EPlannerDatabaseEntities();
// GET: /Message/
public ActionResult Index(int UId)
{
/* linq expressions */
return View();
}
}
}
when the parameter has passed in, i want to;
*Select from EventUser table where UID=UserID and join the result with Event and EventObject tables by using EventID attribute.
*Finally by using the final result i need to display every event's infomation that user has invited; like CreatedBy , StartTime, Budget,other users,objects etc..
i'm new to mvc and viewmodel concept.I heard that viewmodel concept can help with these situations.can i overcome this problem by using viewmodel concept.if yes what are the things i need to add in view model?? otherwise what are the other ways to do this?
one way i can see of doing this is creating a custom return object and using EF to join all the tables together. Example
public class MyObject{
public DateTime DateCreated{get;set}
// add remaining properties here
// properties to get back
}
then in code you would use Entity Framework to create a joined data set into a nice list of objects. Example:
var results = (from b in bla join bla2 in (Some Second Query Here)
from SomeSecondQueryHere
where cond1 and cond2 Select new MyObject{
// add properties in here})
where you would replace the bla and bla2,etc with respective table names needed. Then all you need to do is
return View(results);
And the changes will be accessible in the View
If you question is regarding querying with an ORM like Entity Framework, you need to post your entities, not your table schemas. The whole purpose of an ORM is to abstract away the underlying database structure, so while the schema will often be similar to the entity class, it can also be quite different. As a result, I'll have to make assumptions about your entity classes.
To query everything, you just need something like the following:
var events = db.Events.Where(m =>
m.EventUsers.Any(u => u.UserID == UId && u.IsAccepted)
).Include(m => m.EventObjects);
That assumes entity classes along the lines of:
public class Event
{
...
public virtual ICollection<EventObject> EventObjects { get; set; }
public virtual ICollection<EventUser> EventUsers { get; set; }
}
public class EventUser
{
...
public int UserID { get; set; }
public bool IsAccepted { get; set; }
}
You end up with an enumerable of Event. If you need to access the EventObjects for an individual event, you have to use the appropriate collection property. For example:
foreach (var item in events)
{
foreach (var obj in item.EventObjects)
{
// do something with `obj` (an invidual `EventObject` instance)
}
}
If you need the actual User object, you're better object querying that first and including related Events and EventObjects:
var user = db.Users.Include("EventUsers.Event.EventObjects").SingleOrDefault(m => m.UserID == UId);
That assumes entities like:
public class User
{
...
public virtual ICollection<EventUser> EventUsers { get; set; }
}
public class EventUser
{
...
public virtual Event Event { get; set; }
}
public class Event
{
...
public virtual ICollection<EventObject> EventObjects { get; set; }
}
With that method, however, there's no way to filter the included Events by whether they're accepted or not. There's a potential way around that, but it requires disabling lazy-loading of EventUsers entirely and complicates querying the information you need. If you need to go that route, see: https://msdn.microsoft.com/en-us/data/jj574232.aspx#explicitFilter.
Otherwise, you can just exclude non-accepted events before iterating over the collection:
var events = user.EventUsers.Where(m => m.IsAccepted).Select(m => m.Event);
Really you don't need a view model, per se, for any of this. As you can either pass the lists of events (which will include any related EventObjects) or the the single user instance (which includes related events and related EventObjects) directly to your view.
A very high level description of how to solve your scenario using Entity Framework would be something like this:
First you've got to create a series of entity data objects that will represent your tables in the EF data model using EF Code first techniques.
Then you create DbContext objects with DbSets for your previously created entities.
Then you create at least one Service class that will have a property representing DbContext and a set of methods encapsulating Linq queries to your entities.
In the MVC controller you call an instance of Service that you previously create and assign it to a property ant Controller's construction time. Finally, in the Action method you should call the correct Service method and pass any result to the view.
( I am assuming this is a small Ad-Hoc system with a handful of tables , an elaborate System with production quality would require using IoC techniques).
This is a very weird architecture. Please bear with me.
We have an existing tiered application (data, logic/service, client).
The latest requirement is that the service layer should access two data sources!!!! (no other way around)
These two data sources have the same DB schema.
As with most tiered architectures, we have read and write methods like:
IEnumerable<Product> GetAllProducts(),
Product GetProductById(ProductKey id),
IEnumerable<Product> FindProductsByName(string name)
the product DTOs are:
class Product
{
public ProductKey Key { get; set;}
...
}
class ProductKey
{
public long ID { get; }
}
We narrowed it down to two possible solutions:
Alternative 1:
Add a parameter into the read methods so that the service knows what DB to use like so:
Product GetProductById(ProductKey id, DataSource dataSource)
DataSource is an enumeration.
Alternative 2 (my solution):
Add the DataSource property to the key classes. this will be set by Entity Framework when the object is retrieved. Also, this will not be persisted into the db.
class ProductKey
{
public long ID { get; }
public DataSource Source { get; } //enum
}
The advantage is that the change will have minimal impact to the client.
However, people dont like this solution because
the DataSource doesn't add business value. (My response is that
the ID doesn't add business value either. Its a surrogate key. Its
purpose is for tracking the persistence)
The children in the object graph will also contain DataSource which is redundant
Which solution is more sound? Do you have other alternatives?
Note: these services are used everywhere.
What I would suggest is door number 3:
[||||||||||||||]
[|||||||||s! ]
[||||nerics! ]
[ Generics! ]
I use a "dynamic repository" (or at least that is what I have called it). It is setup to be able to connect to any datacontext or dbset while still being in the same using block (i.e. without re-instantiation).
Here is a snippet of how I use it:
using (var dr = new DynamicRepo())
{
dr.Add<House>(model.House);
foreach (var rs in model.Rooms)
{
rs.HouseId = model.House.HouseId;
dr.Add<Room>(rs);
}
}
This uses the "default" dbcontext that is defined. Each one must be defined in the repository, but not instantiated. Here is the constructor I use:
public DynamicRepo(bool Main = true, bool Archive = false)
{
if (Main)
{
this.context = new MainDbContext();
}
if (Archive)
{
this.context = new ArchiveDbContext();
}
}
This is a simplified version where there are only two contexts. A more in depth selection method can be implemented to choose which context to use.
And then once initialized, here would be how the Add works:
public void Add<T>(T te) where T : class
{
DbSet<T> dbSet = context.Set<T>();
dbSet.Add(te);
context.SaveChanges();
}
A nice advantage of this is that there is only one spot to maintain the code for interacting with the database. All the other logic can be abstracted away into different classes. It definitely saved me a lot of time to use a generic repository in this fashion - even if I spent some time modifying it at first.
I hope I didn't misunderstand what you were looking for, but if you are trying to have one repository for multiple data sources, I believe this is a good approach.
What is the best way to move child entities from one parent entity to another? Is there a method inside the ObjectContext or DbContext that allows us to accomplish this?
public class Person
{
public int PersonId
public ICollection<Car> Cars
}
public class Car
{
public int CarId
public string Color
}
EDIT: I'm currently using EF 4.0 model first with POCO template.
I'd say what you want to accomplish is changing the owner of the car in this example. If there are no serious cons against adding a back reference to Person in the Car i'd go with something like:
public class Car
{
...
public virtual Person Owner { get; protected set; }
public void ChangeOwner(Person newOwner)
{
// perform validation and then
Owner = newOwner;
// maybe perform some further domain-specific logic
}
}
NOTE: the protected setter is to enforce calling the ChangeOwner method by external consumers. EF wil be able to set it properly thanks to the autogenerated proxies for POCO classes (assume you use them).
EDIT:
In case there is no possibility to add a back reference to Person, you still have have the same goal looking from the domain logic perspective. You just want to change owner of a car. Such operation involves two entites so i'd probably go with a method placed somewhere outside the entity (regardless of where it should be placed in a well designed system):
public void ChangeCarOwner(Person originalOwner, Person newOwner, int carId)
{
Car car = originalOwner.RemoveCarOwnership(carId);
newOwner.AddCarOwnership(car);
}
public class Person
{
...
public Car RemoveCarOwnership(int carId)
{
Car car = this.Cars.Single(c => c.Id == carId);
this.Cars.Remove(car);
return car;
}
}
This is just a conceptual piece of code and it most certainly can be written better (making sure the old owner actually owns the car etc.), but i just wanted to present an idea of how would i approach it. I also ommited the implementation of AddCarOwnership cause i suppose it's pretty strainghtforward. I introduced those methods cause adding and removing ownership may trigger some further logic "inside" a particular person.
With modern EFCore, you can do this very simply by Attaching the new Parent entity, which contains Children with IDs in them. It will reassign the FK of the child (or create it if no ID is specified), and create the new Person, all in one go
Ex:
var newOwner = new Person {
Cars = new List<Car> {
new Car { carId = theCarToMove.carId }
}
};
Context.Attach(newOwner);
await Context.SaveChangesAsync();
Beware that Attach can cause problems if your Context isn't truly transient, but as a bandaid you could always clear the ChangeTracker before attempting an Attach
EDIT: After trying this, I found that for some DB providers, it doesn't work directly. Instead, try:
foreach(var car in carsToMove)
{
Context.Attach(car);
car.Owner = newOwner;
}
Context.Attach(newOwner);
await Context.SaveChangesAsync();
Order matters when using Attach. The SQL query built by EFCore builds in reverse of the order you set it up in C#. If you attach the newOwner before the cars, the CREATE query is the last thing in the SQL, after the UPDATE for the cars. If this is the case, the cars can't UPDATE to the new OwnerId, because the newOwner did not have an ID at that point in the query. I believe this is also what's happening with the first code block, with some providers
I am using RIA Service in our Silverlight application. Database entities are not directly exposed to a client but I have a set of POCO classes for it. Then in CRUD methods for these POCO classes they are converted to database entities and saved to database.
The problem arises on the server side when client creates 2 new POCO entities which are related. Insert method is called on the server for each POCO entity separately and I may create corresponding new database entities there and add them to object context. But I see no way to add relation between these created database entities. Is there a solution for that?
For example, I have these 2 POCO entities (simplified):
[DataContract(IsReference = true)]
public partial class Process
{
[DataMember]
[Key]
public string Name
{
get; set;
}
[DataMember]
public long StepId
{
get; set;
}
[DataMember]
[Association("StepProcess", "StepId", "Id", IsForeignKey=true)]
public Step Step
{
get; set;
}
}
[DataContract(IsReference = true)]
public partial class Step
{
[DataMember]
[Key]
public long Id
{
get; set;
}
[DataMember]
public string Name
{
get; set;
}
}
And I have these 2 Insert methods in my domain service class:
public void InsertProcess(Process process)
{
var dbProcess = new DBProcess();
dbProcess.Name = process.Name;
//dbProcess.StepId = process.StepId; Cannot do that!
this.ObjectContext.AddToDBProcess(dbProcess);
}
public void InsertStep(Step step)
{
var dbStep = new DBStep();
dbStep.Name = step.Name;
this.ObjectContext.AddToDBSteps(dbStep);
this.ChangeSet.Associate<Step, DBStep>
(step, dbStep, (dto, entity) =>
{
dto.Id = entity.Id;
});
}
Client adds a new Process, then creates and adds a new Step to it and then calls SubmitChanges(). Process.StepId is not filled with a correct value as there is no correct Step.Id for the newly created step yet, so I cannot just copy this value to database entity.
So the question is how to recreate relations between newly created database entities the same as they are in newly created DTOs?
I know about Composition attribute but it is not suitable for us. Both Process and Step are independent entities (i.e. steps may exist without a process).
There are two ways to solve this:
Have each call return the primary key for the item after it is created, then you can store the resulting PKey in the other POCO to call the second service.
Create a Service method that takes both POCOs as parameters and does the work of relating them for you.
Thanks, although both these suggestions are valid but they are also applicable only for simple and small object hierarchies, not my case. I end up using approach similar to this. I.e. I have a POCO to database objects map. If both Process and Step are new, in InsertProcess method process.Step navigation property is filled with this new step (otherwise StepId can be used as it referenced to existing step). So if this process.Step is in the map I just fill corresponding navigation property in DBProcess, otherwise I create new instance of DBStep, put it to the map and then set it to DBProcess.Step navigation property. This new empty DBStep will be filled in InsertStep method later.