edmx mapping questions - c#

I have a Branch table that contains:
company_id, is_deleted, branch_id, branch_name, branch_code
company_id - used in order to determine which company owns the branch.
is_deleted - rows where is_deleted=true are logically deleted and I don't want to return them in my queries.
I have to map thos fields to class Branch. Class Branch have the following members:
BranchId, BranchName, BranchCode
Should I add IsDeleted member in order to map the is_deleted field? Can I filter rows with is_deleted=true if I will not map this field?
Should I add CompanyId member in order to map the company_id field? I have many tables with company_id field since it decide whice company own the row. Can I prevent adding CompanyId member when mapping those tables? When inserting, I need to supply CompanyId - I really prefer to supply it externaly and not from the Branch object.

So now you have a concrete example so we can continue in discussion from your previous question where I described some basic information about mapping to existing objects.
Should I add IsDeleted member in order
to map the is_deleted field? Can I
filter rows with is_deleted=true if I
will not map this field?
It is possible. It's called conditional mapping where your is_delete column will be used as a filter in the mapping. It has pros and cons:
Pros:
The filter is applied every time you query the entity set including a lazy loading and an eager loading. You will never get an entity with is_deleted = 1.
Cons:
You can't map is_deleted as a property in the entity. This is one global disadvantage for all columns used to support conditional mapping, table per hierarchy inheritance and independent associations - they can't be exposed as properties. So how would you soft delete your entity if you don't have the column exposed and you can't set it in the application? The only solution for this is stored procedure mapped to delete operation for your entity - btw. it is probably the best solution if you want to do soft / logical deletes because otherwise accidental call of DeleteObject on the context or a set will do hard delete in the database.
You can't map multiple conditional entities to the same table. It means you can't have conditionally mapped both undeleted and deleted entity. This can be handled by table per hierarchy inheritance.
Btw. as I know this is not available in DbContext API (EF 4.1).
Should I add CompanyId member in order
to map the company_id field? I have
many tables with company_id field
since it decide which company own the
row. Can I prevent adding CompanyId
member when mapping those tables? When
inserting, I need to supply CompanyId
- I really prefer to supply it externaly and not from the Branch
object.
Do you have a relation between the company table and the branch table in your database? In such case your Branch entity must use either independent or foreign key association with the Company entity. Association by default creates navigation property on both related entities so your Company entity will have collection of related Branches and your Branch will have a reference to the Company it belongs to. Navigation properties are the main way how to create relations in the object world. So if you want the Branch to belong to any Company you will either assign the Company to the property in the Branch or add the Branch to the collection of branches in the Company. That is the theory - it is little bit more complex with EF when using detached objects.
To avoid some problems EFv4 introduced foreign key association where dependent entity doesn't have only navigation property but also foreign key property (your country_id). You can create relation simply by assigning this property with the id of related Country.
I have already answered separate question describing differences between Independent and Foreign key associations.
Conclusion: You must use either navigation property or foreign key property to create relation between object - both these artifacts are mapped in the entity.
Now example which will also show some details you asked me yesterday. This example shows following features:
Conditional mapping (When is_deleted = 0 in mapping details)
Independent association (I have also already described how to change Independent association to Foreign key association). If you are creating the model from existing database you can check Include foreign key columns in the model in Update wizard and it will use foreign key associations instead of independent associations in the whole model.
Navigation properties on both sides of the relation
Renaming properties in conceptual model (check mapping details where nice names are mapped to database names)
Changing accessibility of the Id property setter. I already answered similar question where this was required with POCO T4 template but same must be done for custom business objects.
Support for lazy loading - check virtual keyword used in business object's code for navigation properties.
Support for tracking proxies - check virtual keyword used in business object's code for scalar properties.
Related mapped business objects will look like:
public class Branch
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual string Code { get; set; }
public virtual Company Company { get; set; }
}
public class Company
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Branch> Branches { get; set; }
}
And context using these custom business objects can look like:
public class Context : ObjectContext
{
public Context()
:base ("name=ModelContainer")
{
Companies = CreateObjectSet<Company>();
Branches = CreateObjectSet<Branch>();
ContextOptions.LazyLoadingEnabled = true;
ContextOptions.ProxyCreationEnabled = true;
}
public ObjectSet<Company> Companies { get; private set; }
public ObjectSet<Branch> Branches { get; private set; }
}

No, you're going to need the field visible if you want to do something like filter on it, unless you use Stored Procedures.
I don't really understand this one. Why would you NOT want company_id visible if you need to use it when inserting? It's not going to hurt anything if it's there. :)

Related

Prevent underposting attack by making virtual navigation properties [Required] in Entity framework models

Is it necessary to add [Required] annotation to either foreign key or virtual navigation property of the Entity framework model in order to prevent under posting attack?
I have the below models. CompanyId is the foreign key and Company model has CompanyName as the [Required] property. Minding that foreign key is a not nullable int Is there a risk of under posting attack, either if the foreign key or virtual navigation property of Employee model is not marked [Required]. If there is a risk, does add [Required] on either of them have any negative impact? Because I have enabled Lazy loading and adding [Required] on virtual navigation property necessitates initialising them before saving Employee model.
[Table("Employee")]
pulic partial class Employee : Model
{
public override int Id {get;set;}
public int CompanyId {get;set;}
public string Name {get;set;}
[Required]
public virtual Company Company {get;set;}
}
[Table("Company")]
public partial class Company : Model
{
public override int Id {get;set;}
[Required]
public string CompanyName {get;set;}
public virtual Icollection<Employee> Employees {get;set;}
}
This Entity framework model has a corresponding view model called EmployeeViewModel.
public class EmployeeViewModel
{
public int Id {get;set;}
public string Name {get;set;}
public int CompanyId {get;set;}
}
If your model is not exposed directly through your API then it will not specifically prevent an Under-Post, where this property is not included in a post unless your API also validates this constraint.
Adding [Required] to your navigation properties will however set the underlying ForeignKey column in the database as Not Nullable if you are using the Code First data schema management approach. The added protection of the underlying FK does mean that the data cannot be saved without a value for this field, which will prevent an Under-Post of a PUT (SQL INSERT) but not specifically a POST (SQL UPDATE).
You mentioned in comments that this model is not exposed directly through your API, but contradicted that with a report from a Security scan that indicates that it is, to accurately identify your specific issues would require more information about your actual API.
In MVC the, the controllers will validate that all members annotated with the RequiredAttribute are provided as the input for POST operations. If a property is not provided, then the call will fail. This is described in this post What does it mean for a property to be [Required] and nullable? with some background from Brad Wilson from the ASP.Net team responsible for this feature.
Is it necessary to add [Required] annotation to either foreign key or virtual navigation property of the Entity framework model in order to prevent under posting attack?
Only if the model is exposed to the API and only if you do not want to allow the client to POST without the required FK value or the related Entity. This is highly subjective to your implementation but if your default handling of an omitted navigation property value on a POST is to delete the existing data, or to clear the FK link, then yes, you would want to ensure that the FK or the navigation property was annotated as [Required].
It would be smarter to ignore the navigation properties that are not posted instead of setting the FKs to null.
If there is a risk, does add [Required] on either of them have any negative impact?
The negative impact is that it will require all POSTs on the type to include this information, even if the interface does not allow the selection or manipulation of the related data.
There is a Risk that this can actually create a paradoxical Over-Post scenario where the client holds older/stale data and sends that after the API has already received an updated version of the same data from another user or view that did allow access to the extra information.
To mitigate this risk, make sure that you have implemented concurrency tokens or some other solution to manage concurrency.
One alternative (potentially radical) solution to this is to not allow or not support POST of nested data at all and only operate on the top level of data that is provided. This would still allow you to specify [Required] on the FK properties but not allow or require the whole linked entity to be provided as well. This will require a greater degree of calls from the client to the API to manage the state of richly nested objects but it can also simplify the client and reduce the bytes going across the wire.

Can I have two foreign keys from the same table key in a table-per-hierarchy inheriting table somehow?

.NET Core 2.2:
I have a table-per-hierarchy (TPH) inheritance and I have customer and agent as inheriting from membership. Now I try and create a purchase order and want to add both their membership Ids as foreign keys but I can't, because that would mean putting MemberID twice as foreign keys in my code first model:
[ForeignKey("CustomerModel")]
public string MemberId { get; set; }
[ForeignKey("AgentModel")]
public string MemberId { get; set; }
Do I have to split these into two tables instead of using inheritance? Can I just rename one of these from MemberId to AgentID for example or does it need to match the foreign key's name?
In Databases it can rarely happen that you have several similar entries. Like Multiple Foreign keys from the same table. In these rare cases, they are also not a full N:M relationship, so they can not (and should not) be resolved like that. You have a valid examples for this rare case. After all, a Agent can also be a Customer in any given Transaction. Including ones where he is also the agent.
I always try to work from wich table a Foreign Key is, into the Column names. But in such a case, you could work the function into it.
public string Fk_CustomerMemberId { get; set; }
public string Fk_AgentMemberId { get; set; }
I have no idea how to get EF to properly map them given the different names, however. I would guess you put that into the [ForeignKey("CustomerModel")] Atribute. Maybe something like ("CustomerModel.MemberID") or ("CustomerModel","MemberID")?

Entity Framework: include foreign key ID in entity?

I have read in many places that one should (usually) have the foreign key id in an entity because it is a database detail, and in fact, EF manages it pretty well by adding an EntityName_Id column to the corresponding table.
public class Order
{
public decimal Total { get; set; }
public int? InvoiceId { get; set; }
public Invoice Invoice { get; set; }
}
public class Invoice
{
public ICollection<Order> Orders { get; set; }
}
In this example, an Order has an optional relationship with Invoice. When showing info about Order, I am interested in knowing whether or not it has an Invoice.
The problem is that if I don't have the ´InvoiceId´ property in my entities, I have to check the whole related entity just to check if it exists, whereas having that property in my entity would allow me to check it for null, which is "free" in the sense that the entity is already loaded.
Is that the only use of having foreign key Id properties in entities? Am I missing something here?
You can check entity framework documentation to see answer for your question:
https://msdn.microsoft.com/en-US/data/jj713564.aspx
It says:
With foreign key associations, you can use either method to change,
create, or modify relationships. With independent associations, you
cannot use the foreign key property.
Use cases provided in article:
Update foreign key relationship -- this can cause issues if you update navigation property and foreign key at the same time and reference different objects.
By assigning a new value to a foreign key property, as in the
following example.
course.DepartmentID = newCourse.DepartmentID;
Remove foreign key relationship. This only works if there is no associated object with FK property, that is in state 'Added'.
The following code removes a relationship by setting the foreign key
to null. Note, that the foreign key property must be nullable.
course.DepartmentID = null;
Apart from these cases you can use it to check if your reference is not null/null without really lazy-loading related entity like you said:
var isNull = course.DepartmentID == null;
I believe it's pretty much it. From my point of view, benefits from using FK property are too small to rely on them.

Undo / Redo with change tracking in Entity Framework

I'm trying to implement an Undo / Redo feature based on entity framework with POCO entities. Therefor after each change that I want to track I call the ChangeTracker for any modification to entities an I call the ObjectStateManger for changes to relationships (i.e. navigation properties). I store all the changes with the current and previous values to be able to go back and forth in the history.
My problem now is that for entities with a navigation property and a corresponding foreign key these two values are not properly synced if the referenced entity was newly added to the DbContext.
To clarify: Say I have these classes:
public class EntityFilter
{
[Key]
public Guid ID { get; set; }
public virtual ICollection<EntityConnection> IsSource { get; set; }
public virtual ICollection<EntityConnection> IsSink { get; set; }
//other stuff
}
public class EntityConnection
{
[Key]
public Guid SinkFilterID { get; set; }
[ForeignKey("SinkFilterID")]
public virtual EntityFilter Sink { get; set; }
public Guid SourceFilterID { get; set; }
[ForeignKey("SourceFilterID")]
public virtual EntityFilter Source { get; set; }
//more stuff
}
EntityConnection is basically a many-to-many relationship between filters, but it actually contains more fields which is why I cannot get rid of it. Also I want to be as general as possible and not depend on our actual data model.
The problem arises if I add a new filter and then connect it to an existing filter (could also be a new one). Undoing the connection is still ok, but when I try to redo my program will crash. I can see in the restored connection that the foreign key SinkFilterID has the correct value but Sink is null (the same might happen for source, depending on the direction of the connection). Manually calling DetectChanges makes no difference. Adding a connection between two existing filters (i.e. they are already stored in the db before) is no problem.
The detected changes for a new connection of this type only contain entity changes from the ChangeTracker and no relationship changes from the ObjectStateManger. I guess this is because the relationship is already handled by the foreign key, which is included in the properties from PreviousValues.
I've read that entities in the EntityState.Added state get temporary keys and that change tracking for them is not fully supported. Can I get this to work somehow?
I've tried to check with the MetadataWorkspace if my updated entities have a foreign key and a corresponding navigation property and in that case update it manually via reflection, but I'm not sure what data I actually have to check.
Is there a way to keep foreign keys and navigation properties to added entities in sync? Or do you have any suggestions what I might try?
Thank you very much.
Here is what I ended up with:
I keep a separate list of all the added entities. Then when I have to restore a navigation property that is backed by a foreign key I search that list and manually set the navigation property. The hardest part was to figure out how to check in the data model if this fixup was at all needed and to find the name of the corresponding property.
The overall system still has some flaws for maximum generality but it works quite well for what we need.

Do unidirectional associations lead to non-required foreign key fields through NHibernate

Update
Added mappings below
Question summary
I have a database with many required foreign key fields and a code base with many unidirectional associations. I want to use NHibernate, but as far as I can tell, I either have to make the foreign key fields in the database NULLable (not a realistic option) or change the associations to bidirectional (not ideal either). Any other options that I've missed?
Backgrounds
I've joined a project that uses NHibernate to map tables 1:1 to so-called "technical" objects. After data retrieval, the objects are mapped to the actual domain model (AutoMapper style,implemented differently). I know that this is an unnecessary step and I want to propose removing it to the team. However, I'm running into an issue.
The domain model contains many unidirectional associations: the Case object has a list of Persons associated with the case, but the Persons do not hold a reference to the Case object. In the underlying database scheme, the Person table has a required foreign key field that references the case Id. The data model:
[ERD]
PERSON
CASE Id* Ids are generated by the DB
Id* <--FK-- CaseId* * denotes required fields
(other) (other)
The domain model looks like this:
public class Person : DomainEntity
{ // DomainEntity implements Id. Non-essential members left out }
public class Case : DomainEntity
{
public virtual IList<Person> Persons { get; set; }
}
Calling session.Save() on a Case leads to a database error (CaseId required when inserting into Person), because NHibernate starts with inserting the Person entries, followed by the Case entry and finishes by updating the CaseId column in the Person entries. If the CaseId column in the database is altered to non-required (allow NULLs), everything works as it should... however, that change is not an option at the moment (the database model is shared by several apps for at least another year).
The only way I have found to get NHibernate to execute the database actions correctly is by changing the association to bidirectional, i.e., by changing Person to
public class Person : DomainEntity
{
public virtual Case Case { get; set; }
}
This would involve significant changes to the existing codebase however, so I would prefer alternatives, if they exist. I've played around with component mappings, but that is a bad fit since most associations in our model are not actual (UML) compositions. Are there any other options that I've missed? TIA!
EDIT
The (Fluent) mapping for Case looks like this:
public class CaseMapping : ClassMap<Case>
{
public CaseMapping()
{
Not.LazyLoad();
Id(c => c.Id).GeneratedBy.Identity();
Map(x => x.Code).Not.Nullable().Length(20);
Map(x => x.Name).Not.Nullable().Length(100);
HasMany<Person>(x => x.Persons)
.AsBag()
.KeyColumn("CaseId")
.ForeignKeyConstraintName("FK_Person_Case")
.Cascade.AllDeleteOrphan();
}
}
If I use SessionSource.BuildSchema for a test database, this generates a Person table with a nullable CaseId column. I have not found a way for this to work with a non-nullable CaseId field without bidirectional associations. The executed (pseudo) SQL statements:
INSERT INTO Case...
select ##identity
INSERT INTO Person /* (all columns except CaseId) */
select ##identity
UPDATE Person SET CaseId = ? WHERE Id = ?;#p0 = 2, #p1 = 1
I think you may be out of luck here. The docs at http://nhibernate.info/doc/nh/en/index.html#collections-onetomany state:
If the column of a association is declared NOT NULL, NHibernate may cause constraint violations when it creates or updates the association. To prevent this problem, you must use a bidirectional association with the many valued end (the set or bag) marked as inverse="true"

Categories