I wanted to start using Entity Framework for my projects. A new project which we'll be starting soon, will have an Employee table. I was initially planning to have a IEmployee Interface, which will be implemented by a Manager and Staff classes, which will allow different functionality but will both store data in the Employee table, with a flag in the table distinguishing them.
If I use DB First, and design my Employee table and then use Entity Framework, i know the .tt file will have a partial class Employee. I could then make my own Manager and Staff classes which implement the partial class Employee. But then how would I store that back in the db using Entity Framework? Could I just do something like
// currentManager would be the manager object
dbContext.Emplyee.Add(currentManager);
dbContext.SaveChanges();
would entity framework be ok with this, even though i'm passing a Manager object into Employee to save? Or is there a better way to do this? And same with retrieving, how could I use Entity Framework to get back a Manager or Staff? Would I need to get a Employee back first and then cast it? Something like
var employee = from employees... // get employee
Manager currentManager = (Manager)employee;
Yes, you can use EF DB first and have multiple classes derived from a common base class that are stored in the same database table.
The "flag" you envision is called a discriminator by EF. It is a column in the table that specifies which subtype the record is an instance of.
With database first, you need to tweak the model generated by EF to get this working, but it's fairly straightforward. I think the easiest way to set this up is to
Create the database model in SQL, being sure the Employee table has a NOT NULL property like EmployeeType that can be used as the discriminator. I think this can be any type, but an int will work fine.
Create your EF model ( Add | New Item... | Data | ADO.NET Entity Data Model ), and map the Employee table (and anything else you need).
Double click the generated .edmx file to open the entity framework designer.
Right mouse click on the canvass and select Add New... | Entity and create a Manager entity that derives from Employee. Repeat for Staff. Your designer should look like this:
Right mouse click on Manager and select Table Mapping. In the mapping details, specify that it maps to Employee and add a condition that is When EmployeeType = 1. Do the same for Staff, but make the mapping When EmployeeType = 2.
Last, you need to delete the EmployeeType property from the Employee mapping.
That being done, you can extend the Manager and Staff classes (that will now be generated by EF as partial classes) to have whatever business logic you want, and do queries/etc with EF via the Employees mapping:
public class Manager : Employee
{
public void customManagerMethod() { }
}
public class Staff : Employee
{
public void customStaffMethod() { }
}
class Program
{
static void Main(string[] args)
{
using (var db = new dbfirstEntities())
{
Manager m = new Manager
{
FirstName = "Joe",
LastName = "Bigshot"
};
Staff s = new Staff
{
FirstName = "Joe",
LastName = "Schmoe"
};
db.Employees.Add(m);
db.Employees.Add(s);
db.SaveChanges();
}
}
}
Related
I'm completely new to Symfony and I'm struggling with the query builder.
I have card entity and transaction entity in manytomany relation.
card entity: (id, card_number, code)
transaction entity: (id, amount, source, destination)
card_transaction: (card_id, transaction_id)
I want to get all the transactions that have a given card number.
We'd typically need to see the source for both of those entity classes.
Have you made a repository class for the CardTransaction entity yet? You could create this to have a reusable query as follows:
<?php
namespace App\Repository;
class CardTransactionRepository extends \Doctrine\ORM\EntityRepository
{
public function findByCardNumber(int $cardNumber): array
{
if ($cardNumber < 1) {
throw new \InvalidArgumentException("Card ID invalid.");
}
$qb = $this->createQueryBuilder("ct")
->leftJoin("ct.card", "c")
->andWhere("c.cardNumber = :cardNumber")
->setParameter("cardNumber", $cardNumber)
;
return $qb->getQuery()->getResult();
}
}
That being said what you're doing is such a simple query/operation you shouldn't need a query builder to do it. This is precisely what entity classes make easy, including Doctrine's ArrayCollection class.
If you're just doing this in a controller or view and have already loaded the Card entity you don't need a query in a repository class. Just iterate $card->getCardTransactions() assuming you've created the appropriate data accessor functions in Card. -- Then again, without seeing the Card and CardTransaction entity sources I couldn't tell you exactly what method names you have (or need).
You can retrieve a Card entity by its card_number property in a controller as follows:
$card = $this->getRepository(Card::class)->findOneByCardNumber($cardNumber);
Ideally you should check it exists too though kind of like this:
if (!($cardNumber = intval($cardNumber))) { // Do something better to validate the card number that has been specified, e.g. a `preg_match()`
throw $this->createNotFoundException("Card not specified.");
}
if (!($card = $this->getRepository(Card::class)->findOneByCardNumber($cardNumber))) {
throw $this->createNotFoundException("Card not found.");
}
I'm working on a simple project which is based on the popular ContosoUniveristy tutorial. I want to extend some of the functionalities present in this tutorial.
I have created a table named School where I keep each schools properties like address, phone number, courses, students and so on. Later I added a foreignKey property named SchoolID to student, course and few other tables.
I have SchoolIndex page with basic layout view where all the schools are listed and user can click one to go to the details page. This details page has a _DitLayout layout with additional menu on the left where one can find links to appropriate informations e.g. contact, courses, students (like the ones stored in School table). _DitLayout is shared by all the contact, courses and students views.
Here I have a problem. When I click school on the SchoolIndex page I want the links in the menu on the left to point to this particular school properties. To do that I would have to somehow pass SchoolID to the layout page (not a good idea?). The other way is to somehow store the SchoolID when running trough views, controllers and actions. So that in the controller i could write
public class SchoolController : Controller
{
private SchoolContext db = new SchoolContext();
public ActionResult Contact()
{
int ID = //here I pass SchoolID;
// I fetch school from the database
SchoolModel school = db.School.Find(ID);
//and I map the properties to the ViewModel
ContactViewModel contact = new ContactViewModel();
contact.Address = school.Address;
contact.Phone = school.Phone;
//etc.
return View(contact);
}
}
Question is: How can I pass or store SchoolID between controllers? YES I need to pass ID not only between actions but also between controllers.
I thought that maybe i could store this ID in the cookie. Is it a good idea? Is there a better way to do it?
If you want to store data, without keeping it inside the URL (or HTTP request), then you would have to save it either in Session or Cookie.
Personally I would modify the MVC routing to incorporate the school id.
eg: /{schoolId}/{controller}/{action}.
This way schoolId will be available in any action, regardless of the controller.
You can use Session object or TempData to pass data between controllers.
public class SchoolController : Controller
{
private SchoolContext db = new SchoolContext();
public ActionResult Contact()
{
int ID = TempData["SchoolID"];
// I fetch school from the database
SchoolModel school = db.School.Find(ID);
//and I map the properties to the ViewModel
ContactViewModel contact = new ContactViewModel();
contact.Address = school.Address;
contact.Phone = school.Phone;
//etc.
return View(contact);
}
}
public class OtherController : Controller
{
public ActionResult School(int id)
{
TempData["SchoolID"] = id;
return RedirectToAction("/School/Contact");
}
}
i've started learning Silverlight 4 RIA services. i've gone over alot of samples of how to bind data to a grid. but always there object being bound is a simple one with no child tables.
in my DB there's a table for employees and a table for city names (with id field as pk). in the employee table theres a FK to the CityId field.
the first thing i've tried to do was to show a list of employees and their city name.
this i've done in the normal way shown in all the tutorials (create edmx, create domain service and using the datasource window to create the datagrid)
the problems started when i tried binding the name of the city throw the FK between employee (parent entity) and citytypes (child entity)
this line works fine:
<sdk:DataGridTextColumn x:Name="cityCodeColumn" Binding="{Binding Path=CityCode}"
Header="CityCode" Width="SizeToHeader" />
this line does not:
<sdk:DataGridTextColumn x:Name="cityNameColumn" Binding="{Binding Path=CityType.Name}" Header="CityName" Width="SizeToHeader" />
after reading some more i've realized that the the domain service does not pass only the data of the entity selected by the LINQ command, and does not pass child entities info.
unless using the include attribute.
so my question is , is there a pattern of building a silverlight application with out signing all the associations between entities as included?
thanks,
Oren
To have the City information available when binding your Employee record you need to ensure you are marking the City reference with an [Include] attribute in your RIA domain service metadata.
[MetadataTypeAttribute(typeof(MyTestObject.MyTestObject_Metadata))]
public partial class MyTestObject
{
internal sealed class MyTestObject_Metadata
{
// Metadata classes are not meant to be instantiated.
private MyTestObject_Metadata()
{ }
[Include]
public AnotherObject Foo { get; set; }
}
}
You also need to include the references in your query.
var results = this.ObjectContext.MyTestObject.Include(Foo);
Hope this helps.
My scenario:
This is an ASP.NET 4.0 web app programmed via C#
I implement a repository pattern. My repositorys all share the same ObjectContext, which is stored in httpContext.Items. Each repository creates a new ObjectSet of type E. Heres some code from my repository:
public class Repository<E> : IRepository<E>, IDisposable
where E : class
{
private DataModelContainer _context = ContextHelper<DataModelContainer>.GetCurrentContext();
private IObjectSet<E> _objectSet;
private IObjectSet<E> objectSet
{
get
{
if (_objectSet == null)
{
_objectSet = this._context.CreateObjectSet<E>();
}
return _objectSet;
}
}
public IQueryable<E> GetQuery()
{
return objectSet;
}
Lets say I have 2 repositorys, 1 for states and 1 for countrys and want to create a linq query against both. Note that I use POCO classes with the entity framework. State and Country are 2 of these POCO classes.
Repository stateRepo = new Repository<State>();
Repository countryRepo = new Repository<Country>();
IEnumerable<State> states = (from s in _stateRepo.GetQuery()
join c in _countryRepo.GetQuery() on s.countryID equals c.countryID
select s).ToList();
Debug.WriteLine(states.First().Country.country)
essentially, I want to retrieve the state and the related country entity. The query only returns the state data... and I get a null argument exception on the Debug.WriteLine
LazyLoading is disabled in my .edmx... thats the way I want it.
You're doing a join without retrieving anything from it. There are multiple solutions to your problem:
Use Include to load the dependent entities: from s in ((ObjectSet<State>) _stateRepo.GetQuery).Include("Country"). The problem with this approach is that you should expose the ObjectSet directly rather than as a IQueryable if you want to avoid casting.
Use context.LoadProperty(states.First(), s => s.Country) to explicitly load the Country from the database for a given state.
Select both entities in the query: from s in ... join c ... select new { s, c }. You won't be able to access directly the state's Country property but you have it in the anonymous type.
Enable lazy loading.
Your repository implementation is very similar to mine, especially the way you are storing the ObjectContext. It works fine for me, so I don't think it's a conceptual problem.
Try using a static objectcontext (no wrapper) just to see if that fixes the problem. Perhaps there is a bug in your ContextHelper which causes your context to get disposed and recreated.
Im trying to get my head around attaching an entity with a related entity to a new context when I want to update the entity.
I have a Person Table (Generalised to Personnel), which has a LanguageID field. This field is linked as a FK via the EF to another table Language with LanguageID as the primary key (1-M). I need to update a particular Persons language preference, however, the relationship seems to remain linked to the old context as i get a "Object cannot be referenced by multiple instances of IEntityChangeTracker" error on the line marked below. Is there any way to attach the Language entity to the new context as a relationship of the Personnel (Person) entity???
The entities were not detached in the orginal GetPersonnel() method which uses an .Include() method to return the PreferredLanguage
PreferredLanguage is the NavigationProperty name on the Person table...
public static void UpdateUser(Personnel originalUser, Personnel newUser )
{
using (AdminModel TheModel = new AdminModel())
{
((IEntityWithChangeTracker)originalUser).SetChangeTracker(null);
((IEntityWithChangeTracker)originalUser.PreferredLanguage).SetChangeTracker(null);
TheModel.Attach(originalUser);--Error Line
TheModel.ApplyPropertyChanges("Person", newUser);
TheModel.SaveChanges();
}
}
Thanks
Sean
To avoid these sort of problems you should make GetPersonnel() do a NoTracking query.
I.e.
ctx.Person.MergeOption = MergeOption.NoTracking;
// and then query as per normal.
This way you can get a graph of connected entities (assuming you use .Include()) that is NOT attached. Note this won't work if you try to manually detach entities, because doing so schreds your graph.
Hope this helps
Alex