Entity Framework Partial Classes Instantiate by Id / Primary Key - c#

I'm using Entity Framework 4.1. I have a normal model .edmx which maps a Match class to a 'Match' database table and this can be accessed as normal using EF.
However I require custom properties methods for the Match so I extended this using a partial class and I can add my properties etc.
All of this works fine, however I just can't find out how to instantiate an instance of my partial match class by its primary key / id. i.e so I can pass the Id into the constructor and have the object populated with all of its data from the database.
I know we can do the following to populate from calling code:
public Match PopulateforMatchId(int matchId)
{
var match = (from m in _db.Matches
.Include("TeamA")
.Include("TeamB")
.Include("Season")
.Include("Season.Competition")
where m.Match_ID == matchId
select m).FirstOrDefault();
return match;
}
However this is not what I need as this is not self contained within the partial class itself, I need it to populate itself, as other properties in the partial class rely on the object itself having its data in place before they can be calculated.
Anyone have any ideas how i can do this?
Thanks
Kevin

This is wrong way to use Entity framework. Entity framework is not suitable for easy populating existing object. Moreover it demands that entity has internal dependency on the EF context.
How to probably make it work (but I definitely not recommend it):
public Match(int matchId)
{
// You called constructor yourselves = you have unproxied entity without
// dynamic change tracking and without lazy loading
Id = matchId;
// I'm not sure if this is possible in entity constructor but generally it should work
// Get context somewhere - service locator pattern
ObjectContext context = ContextProvider.GetCurrent();
context.Matches.Attach(this);
context.Refresh(RefreshMode.StoreWins, this);
// Now you should have populated Match entity itself but not its navigation properties
// To populate relations you must call separate query for each of them because `Include`
// is possible only when the entity is created and loaded by EF and lazy loading is off
// in this case
context.LoadProperty(this, m => m.TeamA);
context.LoadProperty(this, m => m.TeamB);
Season = (from s in context.Seasons.Include("Competition")
select s).ToList();
}
This is also the example of wrong constructor - constructor should not take such heavy logic. It should be responsibility of some other initialization method.

Related

MVC Mapping layer accessing Service layer

I have designed my application (ASP.NET MVC with EF6) so there is a Mapping layer responsible of holding mappings from Entities to ViewModels back and forth. It is used by the Controllers, so basically the usual Edit action looks like
var entity = DataService.Get(viewModel.Id);
entity = Mapper.MapToEntity(viewModel, entity);
DataService.Update(entity);
The base MapToEntity implementation uses AutoMapper to map the basic properties of the object.
Update() is implemented in EF6 as a function which marks the entity as modified and calls the underlying context SaveChanges().
However, for more complex mappings, like entities with related entities, e.g. an Order, the Mapper class performs more operations. For example, updating the related entity objects (OrderDetails) with the ViewModel related object data, assigning the parent Id (OrderId) to the new child objects, and finally the problematic issue: deleting a child object.
Right now, my specialised Mappers have a reference to the Service Object of the related entity, which has a MarkEntityToDelete function, called just to mark in the underlying DbSet that entity for deleting.
foreach (var orderDetail in viewModel.OrderDetails.Where(d => d.Id != 0 || d.Delete == false))
{
var orderDetailToUpdate = orderDetail.Id == 0 ? new OrderDetail() : _orderDataService.GetOrderDetail(orderDetail.Id);
if (orderDetail.Delete)
{
_orderDataService.MarkLineaAsDeleted(orderDetailToUpdate);
}
else
{
Mapper.Map(orderDetail, orderDetailToUpdate);
if (orderDetail.Id != 0) continue;
orderDetailToUpdate.MantenimientoId = order.Id;
order.OrderDetails.Add(orderDetailToUpdate);
}
}
This is a dependency which is only there because the implementation uses EF6 and the transaction border is inside the Service, so the Mapper cannot directly delete the object, just mark it for deleting.
Although this solution works well in practice, I don't particularly like it very much. I don't think the Mapper object should have a dependency on a Service object in this way, just to mark a child object for deleting.
I tried to use the following code:
entity.EntregasCuenta.Remove(entity.EntregasCuenta.Single(e => e.Id == entregaCuenta.Id));
but it gives a foreign key check error, which is expected, as I don't delete the related entity but the relationship.
So my question is, how should the mapper deal with this situation? Mapping from a ViewModel to the Entity should be done by a Mapping layer, but updating the related entity should be done by a Service. Finally, the Controller responsibility should be only to call the three operations in order, that is, retrieve entity (service), map viewmodel data to entity (mapping layer), update changes (service).
Am I missing something?

How do I get an arbitrary entity stored in the context with Entity Framework?

I am using Entity Framework. I just need to get an arbitrary entity from the context so I can use reflection to look at its properties. I don't need any specific entity, any arbitrary one will do. My call should look like this:
Entity oldEntity = (Entity)_context.Set(newEntity.GetType()).
And some method should go after the last . but I don't see any that would work (find is my best bet but I can't use it since I don't know the ID's of any of the entities).
Edit:
I forgot to mention that the newEntity I am passing in is generated with a CodeDom class, so newEntity will be the same as the entities in the context, but it may have different properties, which is why I have to get an example "old" entity to compare its properties. To clarify this, imagine I have stored a Person entity with the properties:
First name
Last name
Then I want to add a new property, let's say Age. The way I'm doing this is by generating a new Person entity class with CodeDom with Age as an additional property. I have updated the entity class on disk, so I can create a new entity with the extra field, however my SQL table isn't updated.
You don't need an actual instance of a class to look at its properties.
You can just use typeof(...) like typeof(Entity).GetProperties()

Storing run-time data in objects from ObjectSet<T>

I'm very new to this Entity Framework Object Services Overview (Entity Framework), so forgive me if I use the wrong terminology here.
I'm using the EDMX file to connect to an SQLite database. What I'm trying to do is use the ObjectSet<T> normally, to access a collection of objects from a table in the database. However, I want to additionally store some run-time-only data in the objects in that set. In my case, I have a set of devices stored in the database, but upon startup, I want to mark them as "Connected" or "Disconnected", and keep track of this state throughout execution.
Since the (row) types generated by the EDMX are partial I've added another partial definition, and added my public bool Connected property there. This seems to work, I can set it, and future queries provide objects with the same value that I previously set. The problem is, I don't know a) how it is working, or b) whether I can trust it. These doubts come from the fact that these aren't really collections of objects we're dealing with, right?
Hopefully that made sense, else I can provide more detail.
What you're doing is completely safe.
ObjectSet is still a collection of objects. With a lot magic added underneath.
I am not an expert on the internals but here is how I think it works:
The Entity Framework has a StateTracker hat keeps track of all the entities you're working with.
Every class in your EDMX model is required to have a key. EF is using that key internally so that it loads that specific object only once into memory.
var foo = db.Foos.Single(x => x.Id == 1); // foo with Id 1 is unique (in memory)
var foo2 = db.Foos.Single(x => x.Id == 1); // same instance of foo, but with updated values
var foo3 = db.Foos.Single(x => x.Id == 2) // a new unique instance (Id = 2)
bool sameObject = Object.Equals(foo, foo2); // will return true;
At every select the following happens:
Is an instance of class Foo already tracked/does it already exist?
Yes -> update the properties of the existing instance from the database.
No -> create new instance of class Foo (take values from database)
Of course it can only ever update mapped properties. So the ones you defined in the partial class won't be overwritten.
In case you're going to use code first. There is also the [NotMapped] attribute, that makes sure that the property won't be included in the table if you generate a new database from your code first models.
I hope I could clarify some things for you.

Accessing a Common Code Value Table in Generated EF Models

I have a database that uses a custom model for recording lookup valus (i.e. states, types, etc.). So across all of the database tables, there are various columns called something like state_cdv_id which would store an integer and reference the code_value table to get that state's value (i.e. "CA", "AK", etc.).
I would like to map my EF model so that I can access the code values for all of these fields, and I don't want to have to do it manually in partial classes for EVERY entity... that's a lot of repetition. So I want to be able to access my code values like: MyPerson.State and get back the string "CA" for example.
Here's what a single getter would be that I would have to repeat many times if I were to do it manually:
public string State
{
get
{
MyEntityContext c = new MyEntityContext();
return c.CodeValues.Single(cv => cv.Id == RecordStatusCdvId).Value;
}
}
I don't know what the best approach would be: change the T4 templates, add property attributes to certain fields and then programmatically add a getting to those, or something else.
Any help?
If there is a 1:1 relationship between the entity and the code_value table the entity should already have a State property, which by default which will be null by default, you could then fill it in by using an Include on your DB queries:
var foo = context.MyEntities.Include( x => x.State);
Your sample code is terribly wrong because it makes your entity dependent on the context (moreover you don't dispose it). Whole POCO approach just to avoid this (POCO T4 generator and DbContext T4 generator).
If you have relation to lookup table in your database EF will crate for you navigation property. If you don't have such relation in the database and you are using EDMX file you can still create such relation in your model and you will again get navigation property to lookup table. Once you have navigation property you can simply do:
string code = myEntity.State.Code;
But the navigation property must be loaded either by eager loading (as #BrokenGlass described) or by lazy loading.
If you don't like the idea of navigation property and you still want State property to show just code of the state you must understand what does it mean: If you map the entity that way it will be read-only because EF will not be able to transform compound entity back to real tables which must be updated. It is possible to map the entity the way you want but it is considered as advanced (and mostly not needed) scenario which works only if you have EDMX file (not with code first approach). The choices are:
Create database view and map the view to a new entity
Create DefiningQuery manually in EDMX (opened as XML) file and map it to a new entity (once you do that you cannot update your model from database or generate database from model any more)
Create QueryView manually in EDMX (opened as XML) file and map it to a new entity (this requires original entities to be already mapped)
You will have to do that for every table where you want such mapping. Anyway whole that complexity with manually changing EDMX is not needed because you can simply create custom classes like:
public class SomeViewModel // I suppose your main target is to have codes in presentation layer
{
public string SomeData { get; set; }
public string State { get; set; }
}
and use projection query
If you have navigation property:
var data = from x in context.SomeEntities
select new SomeViewModel
{
SomeData = x.SomeData,
State = x.State.Code
};
If you don't have navigation property
var data = from x in context.SomeEntities
join y in context.LookupValues on x.LookupId equals y.Id
select new SomeViewModel
{
SomeData = x.SomeData,
State = y.Code
};

How would one code an entity instance method to persist itself in Entity Framework 4.0?

I'm working with a small model in Entity Framework 4.0. I'd like to have an instance method of the object that represents an entity persist the entity to the database. So instead of from "external" code:
public static void Main(string[] args)
{
using (EFContext ctx = new EFContext())
{
context.AnEntitySet.AddObject(refToTheEntityInstance);
context.SaveChanges();
Instead, I want the instance of the entity to persist itself, where Contact is the entity name.
public ContactInstance : Contact
{
public void Persist(List<AnotherEntity> otherEntityList)
{
using (EFContext ctx = new EFContext())
{
...
ctx.Contacts.AddObject(this); // DOESN'T WORK.
...
...wire up navigation property to collection of AnotherEntity...
ctx.SaveChanges();
I'm doing something wrong. Is this a bad design? It seems to me that an entity, like any object in object oriented design, should "know" how to persist itself.
From a patterns perspective you are trying to introduce the ActiveRecord pattern, which some people love, some people hate. So asking if this is bad design might turn religious very quickly :)
Having said that it is a common pattern, unfortunately it is not natively supported by the EF.
There are a number of problems with your code:
1) ContactInstance can't be treated as a Contact, which is what you appear to be trying to do, in EF, if you have a derived type in the CLR (i.e. ContactInstance) it must correspond to a derived type in the Entity Model too. (i.e. an Entity type called ContactInstance) which I suspect you don't have. I'm guess you have this just to add the Persist method. Another way to do that is in a partial class (EF works fine with partial classes:
public partial class Contact
{
public void Persist(...){}
}
2) Next your code has some issues with Entities potentially being attached to multiple ObjectContexts, for example if you write this code:
Contact c = new Contact();
c.Firstname = ...;
c.Surname = ...;
c.Persist();
c.Surname = ...;
c.Persist();
It will fail - in the second call to Persist() - because an Entity can only be attached to one Context at a time:
The first Persist(). adds the entity to one context.
And the second Persist() will attempt to add the same entity to another context. Exception time!.
The workaround for this is to have an Ambient context somehow, using something like ThreadBound statics or something, but then you have to deal with all sorts of tricky issues.
Anyway the moral of the story is that what you are trying is possible using EF, but it isn't easy, you have to really think through things like ObjectContext lifetime, issues like attach / detach etc.
Hope this helps
Alex James
Former EF team member

Categories

Resources