I'm implementing DAL and BL layers of application.
It is hosted as WCF service, and EF 4 is used as ORM.
We have role based security layer and business rule that only part of object can be updated by some particular role.
Here is simplified example of problem:
We have such DTOs:
MainType
{
public Guid ID { get; set; }
public String DoctorField1 { get; set; }
public String DoctorField2 { get; set; }
public String NurseField1 { get; set; }
public String NurseField2 { get; set; }
public DateTime Created {get; set;}
public DateTime Updated {get; set;}
public Guid LastUpdateBy {get; set;}
public List<DetailsType> Details { get; set; }
}
DetailsType
{
public Guid MainTypeID { get; set; }
public Guid SomeIdentityID { get; set; }
public String DoctorDetail { get; set; }
public String NurseDetail { get; set; }
public DateTime Created {get; set;}
public DateTime Updated {get; set;}
public Guid LastUpdateBy {get; set;}
}
This entities are mapped to corresponding DB tables with the same fields.
ID field of MainType is Primary Key;
MainTypeID of DetailsType is Foreign Key to MainType table.
SomeIdentityID of DetailsType is FK to some other entity that is not important for this sample.
MainTypeID SomeIdentityID is complex primary key for DetailsType table.
I have graph of such objects (1 main and list details), and determined role of user who performs update operation.
My task is:
IF current user has role Doctor - update Doctor fields in Main object and all Details objects, insert new details objects.
IF current user has role Nurse - update Nurse fields in Main object and all Details objects.
save current date to Updated field
save current user id to LastUpdateBy field
do not modify Created field and any other field that are not updated by this role.
So for example if I have user with role Doctor I should do following:
Update DoctorField1, DoctorField2, Updated, LastUpdateBy in MainObject
Update DoctorDetail, Updated, LastUpdateBy in every details object
DO NOT modify any other fields.
Currently we have implementation that reads full graph for MainObject, makes necessary modifications and saves in back to DB.
This solution works too slow and I need to find a way to improve it.
Currently I know clearly how to do that by RAW SQL, but this will be my solution in case nothing else will help.
How can I make Entity Framework to update only needed fields and ignore another.
I have some successful results with ApplyOriginalValues and ApplyCurrentValues methods for String fields.
Idea was to assign some fictive value to property in both objects, for example string "##$%&##$%&##$%&##$%&##$%&", and EF then treats them as not modified properties during saving changes.
However this trick does not work with Boolean, Int32 and Decimal values.
I should use some simple approach to all objects.
I will appreciate any ideas and thoughts about this problem.
If you have such specific requirement you should start by modifying your WCF service to not accept fields which user cannot modify. That leads to two simple DTOs:
public class MainTypeUpdateDto
{
public Guid ID { get; set; }
public String Field1 { get; set; }
public String Field2 { get; set; }
public List<DetailsTypeUpdateDto> Details { get; set; }
}
public class DetailsTypeUpdateDto
{
public Guid MainTypeID { get; set; }
public Guid SomeIdentityID { get; set; }
public String Detail { get; set; }
}
All other fields either cannot be updated or should be handled by your server side logic.
Now when you receive dtos you can map them back to real entity objects. Based on user role you will know which fields and details you must set. You have two options to force EF to save only fields you want:
First create object graph with MainType and related details. Set only Ids in these entities and attach MainType entity to context. After that set all updatable fields to current values. Do not change state of any entity.
Create object graph with MainType and all related details and set all Ids and all updatable fields. After that attachMainType` entity to the context and manually set state for each modified property (on each entity).
You can need some additional logic if user can also remove or add details.
Related
I'm trying to map a relation N:N for an entity which has some other information. In fact, to brief you better I have the following scenario:
A user can apply as many times as he wants for a exam and this application saves the final result. (That's why i didn't map the key with this to classes)
Looking for this over the internet I found some solutions regarding the creation of Id properties to save information about the Foreign Key besides the property itself. As I don't agree with this solution because I don't believe that we have to change our Model to satisfy ORM needs, I would like to know if you guys have another solution for me.
The following code is a piece of the classes I want to map. Currently, I didn't configure collections in the main classes and when I try to save the application I receive a key violation in the database because it tries to save the User/Exam in the database again.
Sorry if it is a silly question and thanks for your help.
public class User
{
public int Id { get; set; }
public string FullName { get; set; }
}
public class Exam
{
public int Id { get; set; }
public string Description { get; set; }
}
public class Application
{
public int Id { get; set; }
public virtual User User { get; set; }
public virtual Exam Exam { get; set; }
public int Result { get; set; }
}
I have a model called Project
public class Project
{
[Key]
public int ID { set; get; }
public string Title { set; get; }
public string Image { set; get; }
public double? gained { set; get; }
}
I use this model with two stored procedures one returns all the properties and the other without the property gained. And I got this error
The data reader is incompatible with the specified 'Test.Models.Project'. A member of the type, 'Gained', does not have a corresponding column in the data reader with the same name.
I don't want to write separate models for each stored procedure.
How to solve that please ?
The datareader is kind of dumb in the sense that it will only match what was sent back to it. If a column is missing, it fails, as you can see.
The easiest way to solve this would be to update your second SELECT statement in your stored procedure to pass back a column named gained.
SELECT ID, Title, Image, NULL as gained FROM table
Here, we are passing back no data (NULL) as the gained column. This should make the data reader happy, keep you from needing multiple models and not send back any extra data.
The other possibility would be to use inheritance in your models. Have a base model that does not include gained, and have a second model that inherits from the base model that does include gained.
public class ProjectBase
{
[Key]
public int ID { set; get; }
public string Title { set; get; }
public string Image { set; get; }
}
public class ProjectGained : ProjectBase{
public double? gained { set; get; }
}
I'm using CodeFirst with MVC 3 and have these two classes:
public class Person
{
public int PersonId { get; set; }
[Email]
[Required]
public string Email { get; set; }
public string Passwort { get; set; }
public virtual City City { get; set; }
}
public class City
{
public int CityId { get; set; }
[Required]
public string Name { get; set; }
public virtual IEnumerable<Person> Persons { get; set; }
}
When adding a new person I want to reference a city to this person. Therefore i'm using a SelectList with all cities in my view. The CityId and the object is transferred correctly to the Post-method, but when saving the changes to the database I will have a new object in the city-table (with same name, but new Id).
I suggest there's something wrong with the relations in my models. Maybe somebody can help me.
If you give your Person model an explicit CityId property, then you won't need to retrieve the City object from your repository, you can just assign the CityId value directly to the Person object and save it. For really straightforward views, you don't need to use a viewmodel either, you could receive a Person instance into the POST action method and CityId would already be assigned, assuming the html field in the View has the same name.
This should fix your problem, because you will then know you are explicitly using a CityId that already exists.
Your database will already contain a Person.City_CityId field anyway so you're not creating anything new, just giving yourself more control over the situation. Sometimes you may need to use a [ForeignKey] attribute in the model to connect the Person.CityId property with the virtual property, but using the standard naming convention this shouldn't be necessary.
I've been trying to create model in EF 4.1 to represent a database schema with a single table and column holding foreign keys from two other tables, but have had little luck with both annotations and the fluent API. A sample model is shown here:
public class User
{
...
public virtual ExtendedAttribute ExtendedAttributes { get; set; }
}
public class Account
{
...
public virtual ExtendedAttribute ExtendedAttributes { get; set; }
}
public class ExtendedAttribute
{
public Guid Id {get; set;}
public Guid ItemId {get; set;} // both Account.Id and User.Id stored here
public string Value { get; set; }
}
Currently the configuration for these entities looks something like this for both User and Account modelBuilders:
this.HasOptional(u => u.ExtendedAttributes).WithRequired();
Any thoughts on how to do achieve? Many thanks.
It is even not possible with the database itself and EF will not put any abstraction for that. You must have separate column and navigation property for each entity.
My simplified domain model looks something like this:
public abstract class Entity<IdK>
{
public virtual IdK Code { get; protected set; }
}
public class Contact : Entity
{
public virtual string Name { get; set; }
public virtual Company Company { get; set; }
}
public class Company : Entity
{
public virtual string Name { get; set; }
}
and I've defined a viewmodel:
public ContactViewModel()
{
public Guid Code { get; set; }
public int Version { get; set; }
public string Name { get; set; }
public string Company { get; set; }
public List<SelectListItem> Companies { get; set; }
}
to manage my contacts in a view.
Since I want the user to be able to choose from a list of companies I've added a list of SelectedListItem which will be rendered in my view like this:
<%=Html.ListBoxFor(m => m.Company, (List<System.Web.Mvc.SelectListItem>)Model.Companies)%>
Now, when the user submits my form I remap my viewmodel with my model before I save it.
I populate my Contact and use the id of the ContactViewModel.Company to create an object of type Company to associate with the property of the Contact class.
Since I don't want to fetch the whole company from the database I just fill the id.
When I persist my contact, though, I get an exception: "not-null property references a null or transient Domain.Contact.Company".
What is the best solution to manage lookups and persistence with MVC + Nhibernate?
Do you have any suggestions from your experience?
Unfortunately with NHibernate and lookups you can't just assign the ID property to a new instance of the Company object and then assign that Company object to the Contact.
Generally what I would do is in my repository, assuming that you can't change the Company information when saving a contact is something like this:
public Contact Save(Contact contact)
{
if(contact.Company.Id > 0)
contact.Company = Session.Load<Company>(contact.Company.Id);
Session.SaveOrUpdate(contact);
}
I generally find this allows you to encapsulate the logic of loading the Company and also allows you to keep it all wrapped up nicely in a single session.
Using Session.Load in this manner avoids hitting the database as described here
If you don't do this, what you're essentially saying to NHibernate is that you have a company object which you have assigned an ID and now want to save it with all the properties set to Null or empty string values or whatever and that is not what you want.
Alternatively you could create a Save specific Domain Object that looks like this:
public abstract class Entity<IdK>
{
public virtual IdK Code { get; protected set; }
}
public class SavableContact : Entity
{
public virtual string Name { get; set; }
public virtual IdK CompanyId { get; set; }
}
Which maps directly to the Contact table in your database so that when you Save this entity you can literally just map back the CompanyId from your view model and NHibernate will only save that value back and not care at all about the company objects.
It's a case of working out what works best for you. I personally prefer the first option as the extra bit of logic helps simplify the domain model, however if you're creating and exposing a public API then the second method might make more sense.