What is the best way to add attributes to auto-generated entities (using VS2010 and EF4) - asp.net-mvc-2

ASP.NET MVC2 has strong support for using attributes on entities (validation, and extending Html helper class and more).
If I generated my Model from the Database using VS2010 EF4 Entity Data Model (edmx and it's cs class), And I want to add attributes
on some of the entities. what would be the best practice ? how should I cope with updating the model (adding more fields / tables to the database and merging them into the edmx) - will it keep my attributes or generate a new cs file erasing everything ?
(Manual changes to this file may cause
unexpected behavior in your
application.)
(Manual changes to this
file will be overwritten if the code
is regenerated.)

Generally you'd create what is called partial classes to extend your auto-generated objects.
Adding Attributes to Generated Classes

With the "buddy class" concept, linked above, and data annotations I use this extention method. I forget where I got it, so kudos to the original author.
We use it like
List<ValidationResult> errorList = new List<ValidationResult>();
bool bValid = client.IsValid<Client, ClientMetadata>(ref errorList, false);
public static bool IsValid<T, U>(this T obj, ref List<ValidationResult> errors, bool validateAllProperties = true) where T : IValidatableObject
{
//If metadata class type has been passed in that's different from the class to be validated, register the association
if (typeof(T) != typeof(U))
{
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(T), typeof(U)), typeof(T));
}
var validationContext = new ValidationContext(obj, null, null);
var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(obj, validationContext, validationResults, validateAllProperties);
errors = validationResults;
if (validationResults.Count > 0)
return false;
else
return true;
}

We use partial classes, but if you need them persisted and handled by EF, the "Update Model from Database" option is your best friend.

Related

How to find all managed attached objects in EntityManager (JPA)

Is there a way to get all objects which are currently attached in the entity manager?
I want to write some monitoring code which will report the number of attached objects and their classes.
Meaning finding all objects which were loaded by previous queries and find operations into the entity manager.
I'm using EclipseLink, so a specific solution is good too.
EclipseLink's JPA interface pretty much wraps its native code such that an EntityManager uses a UnitOfWork session underneath (and the EMF wraps a ServerSession). You need to get at the UnitOfWork if you want to see what entities it is managing.
If using JPA 2.0, you can use the EntityManager unwrap method:
UnitOfWork uow = em.unwrap(UnitOfWork.class);
otherwise, use some casting
UnitOfWork uow = ((EntityManagerImpl)em).getUnitOfWork();
From there, the UnitOfWork has a list of all registered (aka managed) entities. You can use the UOW to directly log what it has using the printRegisteredObjects() method, or obtain it yourself using getCloneMapping().keySet().
You can also see deleted objects by using hasDeletedObjects() and then getDeletedObjects().keySet() if there are any, as and the same for new objects using hasNewObjectsInParentOriginalToClone() and getNewObjectsCloneToOriginal().keySet()
you can use JPA in a lot of ways i am still unaware of, and there is a lot going on under the hood in eclipselink that i still do not fully understand, but it looks like it is possible to see into the persistence context. USE THIS CODE AT YOUR OWN RISK. it is only meant to give you a hint that it is possible to inspect the context. (whether the code is right or wrong i'm posting it because it would have helped me when i was trying to decide whether to use eclipselink. there doesn't seem to be much in the way of documentation about how to do this properly.)
public void saveChanges() {
Date now = new Date();
JpaEntityManager jem = em.unwrap(JpaEntityManager.class);
UnitOfWorkImpl uow = jem.unwrap(UnitOfWorkImpl.class);
// inserts
for (Object entity : uow.getNewObjectsCloneToOriginal().keySet()) {
if (entity instanceof IAuditedEntity) {
IAuditedEntity auditedEntity = (IAuditedEntity) entity;
auditedEntity.setAuditedUserId(this.userId);
auditedEntity.setAuditedAt(now);
auditedEntity.setCreatedAt(now);
}
}
// updates
UnitOfWorkChangeSet uowChangeSet = (UnitOfWorkChangeSet) uow.getUnitOfWorkChangeSet();
if (uowChangeSet != null) {
List<IAuditedEntity> toUpdate = new ArrayList<>();
for(Entry<Object, ObjectChangeSet> entry : uowChangeSet.getCloneToObjectChangeSet().entrySet()) {
if (entry.getValue().hasChanges()) {
if (entry.getKey() instanceof IAuditedEntity) {
toUpdate.add((IAuditedEntity) entry.getKey());
}
}
}
for (IAuditedEntity auditedEntity : toUpdate) {
auditedEntity.setAuditedUserId(this.userId);
auditedEntity.setAuditedAt(now);
}
}
// deletions
Project jpaProject = uow.getProject();
boolean anyAuditedDeletions = false;
for (Object entity : uow.getDeletedObjects().keySet()) {
if (entity instanceof IAuditedEntity) {
anyAuditedDeletions = true;
DeletedEntity deletion = new DeletedEntity();
deletion.setTableName(jpaProject.getClassDescriptor(entity.getClass()).getTableName());
deletion.setEntityId(((IAuditedEntity) entity).getId());
deletion.setAuditedUserId(this.userId);
em.persist(deletion);
}
}
}
You can achieve this by inspecting the entities on MetaModel which can be obtained from any EntityManager.
Example usage:
EntityManager em = // get your EM however...
for(EntityType<?> entityType : em.getMetaModel().getEntities())
{
Class<?> managedClass = entityType.getBindableJavaType();
System.out.println("Managing type: " + managedClass.getCanonicalName());
}
This example will print out all of the class types being managed by the EntityManager. To get all of the actual objects being managed, simply query all objects of that type on the EntityManager.
Update:
As of JPA 2.0 you can cache results that will be managed by javax.persistence.Cache. However, with plain JPA there is no way to actually retrieve the objects stored in the cache, the best you can do is check if a certain object is in the Cache via Cache.contains(Class cls, Object pk):
em.getEntityManagerFactory().getCache().contains(MyData.class, somePK);
However, EclipseLink extends Cache with JpaCache. You can use this to actually get the object from the cache via JpaCache.getObject(Class cls, Object id). This doesn't return a collection or anything, but it's the next best thing.
Unfortunately, if you want to actually access objects in the cache, you will need to manage this yourself.
I dont see such an option in the EntityManager interface. There is only a contains(Object entity) method but you need to pass the conrete objects and they are the checked for existentnce in the PersistenceContext. Also looking at the PersistenceContext interface i dont see such an option.

MVC .NET How best to update model with file upload

Using EF4 Codefirst RC and MVC .NET
I have a Strongly typed view I use to for my Insert/Update operations on my Model.
One of the field is a file/Image which is uploaded. I use the below code to do this.
Problem is when editing the form the 2nd time if the user does not select a file the File is set to NULL each time.
What different ways to people over come this.
I can
Not store the image in the DB but on the file system (no possible as since this data is coming from an external API also)
Change my View to have a separate form for editing images?
I have already read
EF4 Code First: how to update specific fields only
http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/81a0ee7d-bbe1-416c-9d84-7a30e04730fa/
and what I really would like is a way to Exclude a field from being updated? Is the only way I can do that with the code below? or is there a nicer way?
public ActionResult Edit(CruiselineEditModel cruiseline, HttpPostedFileBase LogoData)
{
if (ModelState.IsValid)
{
var cl = Mapper.Map<CruiselineEditModel, Cruiseline>(cruiseline);
Cruiseline orgObj = _cl.GetById(cruiseline.Id);
UpdateModel<Cruiseline>(orgObj, "", null, new string[] { "LogoData" });
if (LogoData != null && LogoData.ContentLength > 0)
{
byte[] imgBinaryData = new byte[LogoData.ContentLength];
int readresult = LogoData.InputStream.Read(imgBinaryData, 0, LogoData.ContentLength);
orgObj.LogoData = imgBinaryData;
}
_cl.Save();
}
return View(cruiseline);
}
Create an input ViewModel, then you can have separate annotations on the view model. So, on the get, map entity to view model, on the post map view model to entity. This will give you more flexibility in dealing with issues like this.

MVC 2 and EF4 Self-tracking entities models have bad state on post back

I've got standard Create() Edit() and Delete() methods on my controllers, and I am using the EF4 Self-tracking entities.
When the edit is posted back, the model.ChangeTracker.ChangeTracking = false, and model.ChangeTracker.State = ObjectState.Added, even though I made sure those are set when retrieving the record initially.
Are the self-tracking entities not persisting the ChangeTracker class when the form is submitted? If so, how do I fix that?
public virtual ActionResult Edit(int personId)
{
IContext context = ContextFactory.GetContext();
EntityRepo Repo = new EntityRepo(context);
Person d = Repo.Person.GetById(PersonId);
d.ChangeTracker.ChangeTrackingEnabled = true;
return View(d);
}
[HttpPost]
public virtual ActionResult Edit(int personId, Person item)
{
try
{
if (ModelState.IsValid)
{
IContext context = ContextFactory.GetContext();
EntityRepo Repo = new EntityRepo(context);
// the item is returning these properties that are wrong
//item.ChangeTracker.ChangeTrackingEnabled = false;
//item.ChangeTracker.State = ObjectState.Added;
Repo.Person.Update(item);
Repo.Person.SaveChanges();
return RedirectToAction("Index");
}
}
catch
{
}
return View();
}
Let's start at the beginning.
What are Self-Tracking Entities, exactly?
A Self-Tracking Entity is an entity which can do change tracking even when it is not connected to a ObjectContext. They are useful in times when you must change the entity, but cannot have it connected to an ObjectContext.
So when would I want one, really?
Mostly, when you must have distributed objects. For example, one use case is when you are making a web service which talks to a Silverlight client. However, other tools, like RIA Services may be a better fit here. Another possible use case is for a long-running task. Since an ObjectContext is intended to be a unit of work and should typically not be long-lived, having a disconnected entity might make sense here.
Do they make any sense for MVC?
Not really, no.
Let's look at this a little deeper, and examine what happens when you update an entity in MVC. The general process is like this:
The browser issues a GET request for an update page.
The MVC app fetches an entity, and uses it to build an update HTML page. The page is served to the browser, and most C# objects, including your entity, are disposed. At this point, you can restart the Web server, and the browser will never know the difference.
The browser issues a POST request to update the entity.
The MVC framework uses the data in the POST in order to materialize an instance of an edit model which is passed to the update action. This might happen to be the same type as the entity, but it is a new instance.
The MVC app can update the entity and pass those changes back to the database.
Now, you could make self-tracking entities work by also including the full state of the STE in the HTML form and POSTing that back to the MVC app along with the scalar values on the entity. Then the Self-Tracking Entity might at least work.
But what benefit does this give you? The browser obviously cannot deal with your entity as a C# object. So it cannot make any changes to the entity worth tracking in terms that a Self-Tracking Entity would understand.
U should keep original STE in some hidden field. It's like your custom ViewState. In submit method u must merge original STE and new values.
Use ActionFilterAttribute for it.
Like
public class SerializeOriginalModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var viewResult = filterContext.Result as ViewResult;
if (viewResult == null)
return;
var viewModel = viewResult.ViewData.Model as ViewModel;
if (viewModel == null || viewModel.SteObject == null)
return;
byte[] bytes;
using (var stream = new MemoryStream())
{
var serializer = new DataContractSerializer(viewModel.SteObject.GetType());
serializer.WriteObject(stream, viewModel.SteObject);
bytes = stream.ToArray();
}
var compressed = GZipHelper.Compress(bytes);
viewModel.SerializedSteObject = Convert.ToBase64String(compressed);
}
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionParameters == null || filterContext.ActionParameters.Count == 0)
return;
var viewModel = filterContext.ActionParameters.First().Value as ViewModel;
var serialized = filterContext.HttpContext.Request.Form["SerializedSteObject"];
if (viewModel == null || String.IsNullOrEmpty(serialized))
return;
var type = filterContext.ActionParameters.First().Value.GetType().BaseType.GetGenericArguments()[0];
var bytes = GZipHelper.Decompress(Convert.FromBase64String(serialized));
using (var stream = new MemoryStream(bytes))
{
var serializer = new DataContractSerializer(type);
viewModel.SteObject = serializer.ReadObject(stream);
}
}
}
STE has one very big drawback. You have to store them in session or view state (WebForms). So it is nothing more than "new version of dataset". If you don't store STE you will have one instance for getting data and different for posting = no change tracking.
I think you are missing the idea of Repository. You should not have an Update method in the Repository. After submitting, you should get the item again, apply the modifications and then Save.
I prefer having a service layer between client and Repository. We can always change the strategy with which we merge.
And yes, if you need to persist your STE's between requests, use session or viewstate.
It should be
Repo.Person.ApplyChanges(item);
Repo.Person.SaveChanges();
instead of
Repo.Person.Update(item);
Repo.Person.SaveChanges();
Self Tracking works with ApplyChanges extention method.

Entity Framework 4 , Custom Properties . Add some traitement

I have a class Address Generated by entity Framework.
I Have an propertie AddressID in this class.
I Would like to be able to add some treatement for this prop in the set process.
EX :
public partial class Address
{
public bool _AddressID;
public bool AddressID{get return AddressID;}
set{
if(value == -1) _AddressID = null;
}
}
Thanks
Of course you can't redefine your AddressID in order to put your custom logic in its setter, as you'll get compiler error:
The type Address already contains a definition for 'AddressID'
But no worries, if you take a look at the EF generated code for your EntityObject (let's assume its name is Address) you'll see that every scalar property of generated Address class has its own version of OnPropertyChanging and OnPropertyChanged method. For example, OnAddressIDChanging and OnAddressIDChanged in this case.
As you can see below, there is no default implementation for these two methods, only a declaration. This perfectly provides you the opportunity to execute custom logic
as the property is about to change (PropertyChanging) as well as just after the property
value has changed (PropertyChanged).
// From the designer code for Address class:
partial void OnAddressIDChanging(global::System.Int32 value);
partial void OnAddressIDChanged();
This is how your Entity Model designer code already is look like (hypotetically):
public global::System.Int32 AddressID {
get {
return _AddressID;
}
set {
if (_AddressID != value) {
// OnPropertyChanging method get called here:
OnAddressIDChanging(value);
ReportPropertyChanging("AddressID");
_AddressID = StructuralObject.SetValidValue(value);
ReportPropertyChanged("AddressID");
// OnPropertyChanged get called here:
OnAddressIDChanged();
}
}
}
So all you need to do in order to hook up your custom code is:
public partial class Address {
partial void OnAddressIDChanged() {
if(AddressID == -1) {
AddressID = 0;
}
}
}
By the way, about other posted answers - with all due respect to them - if you want this solution for a production application then you cannot use "Code First" since it merely is a CTP as for now and will be part of the next release for EF, so it cannot be an option.
About customizing default code generation, while this is indeed possible since in VS 2010, Entity Framework itself also uses T4 for designer code generation and we can take advantage of it by changing the T4, But it is an option only if you want to fundamentally change how the entity classes are generated in general and you cannot use it for customizing a setter logic for a specific entity.
Code First in EF4 is an option - it allows you to fully control all of the code. However, another option is to customize the EF4 T4 templates that ship with EF4. If you have certain patterns in your code that you consistently use, this would be a good approach. You can read more about how to customize the templates here: Customizing Entity Classes in VS2010

Entity Framework 4 POCO entities in separate assembly, Dynamic Data Website?

Basically I want to use a dynamic data website to maintain data in an EF4 model where the entities are in their own assembly. Model and context are in another assembly.
I tried this Entity Framework 4 + Self-Tracking Entities + ASP.NET Dynamic Data = Error
but get an "ambiguous match" error from reflection:
System.Reflection.AmbiguousMatchException was unhandled by user code
Message=Ambiguous match found.
Source=mscorlib
StackTrace:
at System.RuntimeType.GetPropertyImpl(String name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
at System.Type.GetProperty(String name)
at System.Web.DynamicData.ModelProviders.EFTableProvider..ctor(EFDataModelProvider dataModel, EntitySet entitySet, EntityType entityType, Type entityClrType, Type parentEntityClrType, Type rootEntityClrType, String name)
at System.Web.DynamicData.ModelProviders.EFDataModelProvider.CreateTableProvider(EntitySet entitySet, EntityType entityType)
at System.Web.DynamicData.ModelProviders.EFDataModelProvider..ctor(Object contextInstance, Func1 contextFactory)
at System.Web.DynamicData.ModelProviders.SchemaCreator.CreateDataModel(Object contextInstance, Func1 contextFactory)
at System.Web.DynamicData.MetaModel.RegisterContext(Func`1 contextFactory, ContextConfiguration configuration)
at WebApplication1.Global.RegisterRoutes(RouteCollection routes) in C:\dev\Puffin\Puffin.Prototype.Web\Global.asax.cs:line 42
at WebApplication1.Global.Application_Start(Object sender, EventArgs e) in C:\dev\Puffin\Puffin.Prototype.Web\Global.asax.cs:line 78
InnerException:
I came across a similar problem to this recently. It had to do with inheritance in my model. I had a Resource entity that had derived types of Person, Equipment, etc. and in those I had overridden a couple properties, but by mistake gave them different signatures. I'll describe my scenario and hopefully it will help.
To be able to debug deep enough into the framework, and see all the variable values, you will have to disable optimizations:
Link
I was seeing the Ambiguous Match error when registering the Context in Global.asax as you were:
public static void RegisterRoutes(RouteCollection routes)
{
// IMPORTANT: DATA MODEL REGISTRATION
// Uncomment this line to register an ADO.NET Entity Framework model for ASP.NET Dynamic Data.
// Set ScaffoldAllTables = true only if you are sure that you want all tables in the
// data model to support a scaffold (i.e. templates) view. To control scaffolding for
// individual tables, create a partial class for the table and apply the
// [ScaffoldTable(true)] attribute to the partial class.
// Note: Make sure that you change "YourDataContextType" to the name of the data context
// class in your application.
DefaultModel.RegisterContext(typeof(EntityModelContainer), new ContextConfiguration() { ScaffoldAllTables = true });
Stepping into the RegisterContext method, I got to System.Web.DynamicData.ModelProviders.EFDataModelProvider there is section of code that loads all the Entities in the model by traversing the inheritance hierarchy in the constuctor for EFDataModelProvider.
while (objectStack.Any()) {
EntityType entityType = objectStack.Pop();
if (entityType != null) {
// Update the entity set when we are at another root type (a type without a base type).
if (entityType.BaseType == null) {
currentEntitySet = entitySetLookup[entityType];
}
var table = CreateTableProvider(currentEntitySet, entityType);
tables.Add(table);
}
foreach (EntityType derivedEntityType in derivedTypesLookup[entityType]) {
// Push the derived entity types on the stack
objectStack.Push(derivedEntityType);
}
}
I put a breakpoint in here and was able to see that Ambiguous Match was occurring for me when calling CreateTableProvider on my Equipment entity (which was derived from Resource).
Looking back at the Stack Trace from the original exception (which I should have done in the first place!) I put a breakpoint in the constructor for System.Web.DynamicData.ModelProviders.EFTableProvider.IsPublicProperty and watched to see which property/method/whatever was causing the ambiguous match -- for me this ended up being a navigation property called Resources (Resources are themselves a hierarchy) that I had overridden in Equipment.
private static bool IsPublicProperty(Type entityClrType, string propertyName) {
var property = entityClrType.GetProperty(propertyName);
return property != null && property.GetGetMethod() != null;
}
In the partial class for Equipment, I had:
public partial class Equipment
{
public new IEnumerable<Resource> Resources
{
but in the parent class, Resource, Resources was defined as:
public virtual ICollection<Resource> Resources
{
When these Properties are being loaded by the .GetProperty(propertyName) in IsPublicProperty, they have the same name but different signatures (because I had given them different return type by mistake) so it isn't clear which shoudl be loaded based on name alone. I corrected my mistake and made Resources in my Equipment class return an ICollection, and boom -- no more ambiguous match.
Not sure if this will help or not, but if you step through in a similar way you should be able to find exactly what is causing the ambiguous match.