I'm working with Entity Framework 6.0.2 Code First with Sql Server.
I have a base class called Entity and since we can't extend enums I need to redefine a property type for another class called Company, so I'm using the new keyword to hide the base property and redefine it.
public interface IEntity
{
Guid Id { get; set; }
State State { get; set; }
}
public abstract class Entity : IEntity
{
public Guid Id { get; set; }
public State State { get; set; }
}
public enum State
{
Inactive = 0,
Active = 1
}
public class Company : Entity
{
public new CompanyState State { get; set; }
public string SomeOtherProp { get; set; }
}
public enum CompanyState
{
Inactive = 0,
Active = 1,
Extra = 2
}
The problem I get is when Entity Framework is trying to create the DbContext it crashes with this error: "The item with identity 'State' already exists in the metadata collection. Parameter name: item"
I have a workaround: I could change the State propery in Entity class to int and cast the appropriate enum to int, but I think I'll lose type safety / restriction that the enums have.
I'd like to change the metadata info to avoid this error but I don't know how.
This guy here found a solution for some similar problem.
Neither your, nor his solution is nice. It is and remains a hack.
I would go with the solution you already mentioned. Change the state to stateId. And add a State Property to your Entity:
public State State {get {return (State)stateId;}
In your Company override this Property with new:
public new CompanyState State {get {return (CompanyState )stateId;}
But I think best solution would be, to change your inheritance hierarchy. I think either your IEntity should not have a state, or your company should not inherit from Entity.
Just for exposing another way you could also use this model with an hidden backing field and not mapped states
public interface IEntity
{
int Id { get; set; }
State State { get; set; }
}
public abstract class Entity : IEntity
{
protected int InternalState { get; set; }
public int Id { get; set; }
[NotMapped]
public State State
{
get { return (State) InternalState; }
set { InternalState = (int) value; }
}
// Entity is not a POCO class because of this :(
// If we want to hide InternalState this is the only way to map it
public class EntityMap : EntityTypeConfiguration<Entity>
{
public EntityMap()
{
// Properties
Property(t => t.InternalState)
.HasColumnName("State");
}
}
}
public enum State
{
Inactive = 0,
Active = 1
}
public class Company : Entity
{
[NotMapped]
public new CompanyState State
{
get { return (CompanyState)InternalState; }
set { InternalState = (int)value; }
}
[MaxLength(50)]
public string SomeOtherProp { get; set; }
}
public class Employee : Entity
{
[MaxLength(50)]
public string SomeOtherProp { get; set; }
}
public enum CompanyState
{
Inactive = 0,
Active = 1,
Extra = 2
}
Related
I need to audit data in some tables in my server database so my clients can take partial updates (by table/entity). Data in the server database is only edited from the server website.
The audits will be requested by a client: WHERE Id > [Clients Last Id], the server will then do some processing and then return the latest audits to keep themselves up to date.
I can't seem to get to a generic pattern that will work across the board for all of my models:
public class Domain {
public int Id { get; set; }
public string Property1 { get; set; }
public int Property2 { get; set; }
}
Then I think I want to be able to do something like so:
public class DomainContext : DbContext {
public DbSet<Domain> Domain { get; set; }
public DbSet<History<Domain>> DomainHistory { get; set; }
}
This is my problem class taking this route, I want to inherit from Domain so things like property changes and EF migrations (in code first) will 'just work'. But I Cannot derive from 'T' because it is a type parameter
public class History<T>
: T //Cannot derive from 'T' because it is a type parameter
where T : class {
public DateTime CreatedOn { get; set; }
public int CreatedByUserId { get; set; }
public int EntityFK { get; set; }
// This will always be the current version
//public T Entity { get; set; }
// I could store a snapshot of the state at the time of the audit
public string XMLData { get; set; }
}
I don't know if my use of generics is warranted here but I'm basically trying to get to a point where I can do the below so my models play nicely with EF Migrations:
Domain d = GetDomainModel();
History<Domain> dh = new History<Domain>();
dh.Property1 = d.Property1;
dh.Property2 = d.Property2;
How can this be done?
For a basic audit of your entities you can use a base class and intercept the changue type in a override of savechangues in context, like this (sorry of format, i write in mobile):
public class AuditBase
{
//Adapt your requirements, the propertys are exists in db
public datetime creationdate { get; set; }
public datetime modificationdate { get; set; }
public string creationuser { get; set; }
public string modificationuser { get; set; }
}
public class ModelBBDD : AuditBase
{ }
You can override the SaveChanges method of Context. In the method, you can through the ChangueTracker property of Database Class for added or updates dto's, like this:
var dto = entity as auditbase;
if (dto == null) continue;
if (dto.state == entitystate.added)
{
((Auditbase)entity).creationdate = datetime.now;
((Auditbase)entity).creationuser = environment.username;
}
else if( dto.state == entitystate.modified)
...
...
If you can log all the changues of properties, you can trough all the properties of dto with reflection and type in SaveChanges, and save values in log.
This is how the code looks like:
public class Family
{
public int FamilyID { get; set; }
public virtual ICollection<Member> FamilyMembers { get; set; }
}
public class Member
{
public int MemberID { get; set; }
public virtual Family Family { get; set; }
}
public class Bicycle
{
public int BicycleID { get; set; }
public virtual Member Owner { get; set; }
}
public class MyContext : DbContext
{
public MyContext : base () { }
}
So, there is a one-to-one relantionship between Member and Bicycle, and there is one-to-many relationship between Family and Member.
My question is: how do I initialize these foreign keys using my context? All the tutorials on the internet say how to define, but now how to initialize them. Do I simply assign them a correct int value or do I assign them a whole object, and if so, how do I do that? Using LINQ?
#Edit:
Assume there are 2 families: Smith and Johnson. Smith has 3 members: Ben, Amy, Brian. Johnson has 2 members: John and Natalie. How do I initialize all that in my code?
First, fix this:
public class Member
{
public int MemberID { get; set; }
public virtual Family Family { get; set; }
}
That should be enough to configure the relationships. EF will make the keys identity by convention.
You can assign the relationships multiple ways. You can explicitly set FK Ids if you add them to your models, or you can use the navigation properties like this:
var family = new Family { LastName = "Smith", ...};
List<Member> members = new List<Member>
{
new Member(){ Name = "Ben", ... },
new Member(){ Name = "Amy", ... },
new Member(){ Name = "Brian", ... }
};
family.FamilyMembers = members;
context.Family.Add(family);
context.SaveChanges();
To assign a bike to an owner:
var owner = context.Members.Find(ownerId);
var bike = new Bicycle
{
Make = "ACME",
Model = "XXXX",
Owner = owner
};
context.Bicycles.Add(bike);
context.SaveChanges();
EDIT: Yes, it is certainly permissible to add FK to your model. That's the method I use. Like this code:
public class Member
{
public int MemberID { get; set; }
public int FamilyID { get; set; } // EF will automatically make this a FK by convention
public virtual Family Family { get; set; }
}
You need to add annotations if you don't adhere to convention or if you want them for documentation sake:
public class Member
{
public int MemberID { get; set; }
public int FamilyID { get; set; }
[ForeignKey("FamilyID")]
public virtual Family Family { get; set; }
}
See http://www.entityframeworktutorial.net/code-first/foreignkey-dataannotations-attribute-in-code-first.aspx
I prefer the fluent api. It keeps my models cleaner and separates concerns. For simple projects you can add all the fluent code in the OnModelCreating() override in your context, but I prefer to store my entity configurations in a folder under my context (one file per entity) as described here: http://odetocode.com/blogs/scott/archive/2011/11/28/composing-entity-framework-fluent-configurations.aspx
You can actually have EF automatically find your fluent code using the technique described here: https://msdn.microsoft.com/en-us/magazine/dn519921.aspx
Regarding the child collections, yes, you can new it up in the constructor:
public class Family
{
public Family()
{
FamilyMembers = new HashSet<Member>();
}
public int FamilyID { get; set; }
public virtual ICollection<Member> FamilyMembers { get; set; }
}
You don't need to explicitly define a foreign key between Family and Member. Entity Framework will take care of this for you if you require. Because you have already stated this property
public virtual ICollection<Member> FamilyMembers { get; set; }
On the Family class the Foreign key is created during the initial Add-Migration or Update-database. So you don't need the property
public virtual FamilyFK { get; set; }
I created an inheritance hierarchy after a few migrations. Now when I update the database using code first migrations, code-first is not automatically creating the discriminator field. I have since dropped the table and recreated it (using code-first migrations) without any luck. The only thing I can think of is that there are no additional "non-virtual" properties in the derived classes--the inheritance structure was created to enforce a business rule that only a certain derived type can have a relationship with another entity.
Base Type:
public abstract class Process
{
private ICollection<ProcessSpecification> _specifications { get; set; }
protected Process()
{
_specifications = new List<ProcessSpecification>();
}
public Int32 Id { get; set; }
public String Description { get; set; }
public Int32 ToolId { get; set; }
public virtual Tool Tool { get; set; }
public virtual ICollection<ProcessSpecification> Specifications
{
get { return _specifications; }
set { _specifications = value; }
}
}
Derived class (no different/unique scalar properties):
public class AssemblyProcess : Process
{
private ICollection<AssemblyProcessComponent> _components;
public AssemblyProcess()
{
_components = new List<AssemblyProcessComponent>();
}
public virtual ICollection<AssemblyProcessComponent> Components
{
get { return _components; }
set { _components = value; }
}
}
Another derived type
public class MachiningProcess : Process
{
private ICollection<MachiningProcessFeature> _features;
public MachiningProcess()
{
_features = new List<MachiningProcessFeature>();
}
public virtual ICollection<MachiningProcessFeature> Features { get { return _features; } set { _features = value; } }
}
Is code-first not adding the discriminator column in the database because it doesn't see any differences between the derived classes (because of there not being any unique "non-virtual" properties)? If so, how do I get around this? If not, what are some reasons why code-first would not automatically create the discriminator column in the database? I have another TPH structure that works exactly the way it's supposed to.
DbContext:
public LineProcessPlanningContext()
: base("LineProcessPlanning")
{
}
public DbSet<Component> Components { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Feature> Features { get; set; }
public DbSet<OperationDefinition> OperationDefinitions { get; set; }
public DbSet<PartDesign> PartDesigns { get; set; }
public DbSet<Process> Processes { get; set; }
public DbSet<ProcessPlan> ProcessPlans { get; set; }
public DbSet<ProcessPlanStep> ProcessPlanSteps { get; set; }
public DbSet<ProductionLine> ProductionLines { get; set; }
public DbSet<StationCycleDefinition> StationCycleDefinitions { get; set; }
public DbSet<StationCycleStep> StationCycleSteps { get; set; }
public DbSet<StationDefinition> StationDefinitions { get; set; }
public DbSet<UnitOfMeasurement> UnitsOfMeasurement { get; set; }
public DbSet<Tool> Tools { get; set; }
I also tried creating "dummy" properties that are unique to each derived type. Code migrations added the new properties as columns to the table, but the migration did not create a discriminator column.
I figured out the cause of this in my situation, same as yours. The base class is abstract, therefore EF won't create a TPH table for that class since it can't be instantiated. As a result of the abstract base class, EF will create tables for each of the derived classes, and therefore no need for a discriminator column.
In my case, it was acceptable to remove abstract from the base class. Once I did this, EF's TPH worked as expected.
I have created an MVC 4 application with EF db-first using ADO.NET Entity Data Model.
I've previously been adding data validation and updating constructors directly into the generated Model classes, but as I foresee these tables to be updated I don't want to have to add these all back in, plus I shouldn't be editing these auto generated classes anyway.
Using Metadata.cs and PartialClasses.cs from http://www.asp.net/mvc/tutorials/mvc-5/database-first-development/enhancing-data-validation I'm not sure the best way to update the default constructors for these Model classes.
Here's an example model, simplified.
Within .edmx
public partial class Campaign
{
public Campaign()
{
this.Fees = new HashSet<Fee>();
}
public int ID { get; set; }
public string Name { get; set; }
public string CreatedBy { get; set; }
public System.DateTime CreatedOnDate { get; set; }
public virtual ICollection<Fee> Fees { get; set; }
}
within ParticalClasses.cs [errors as the generated Modal class defines the default constructor]
[MetadataType(typeof(CampaignMetadata))]
public partial class Campaign
{
public Campaign()
{
this.Fees = new HashSet<Fee>();
// Non-Generated
this.CreatedOnDate = DateTime.Now;
}
}
I have other models I would also like to have other constructors with different parameters, so to simplify my question, where do I add constructors for DB first MVC as to no update the generated Model classes?
Not 100% sure about what you are trying to do, but I'll try to answer your question.
First of all, it seems that you are missing the point of the MVC: your link refers to view model validators, but you are talking about data models. Two VERY different things. There's nothing to validate in a data model - those change and are govern by what's going on in the database.
This is what I would do:
1) Create a data layer: this would hold all your entity classes.
2) Create a service layer: this will instantiate and populate the entity classes using either raw sql, or a pattern (repository pattern, for exam).
3) Create your website: this will hold your controllers, view models (they are the ones you want to validate) and views.
For your Campaign class:
public interface IEntity
{
object EntityID { get; set; }
}
public abstract class BaseEntity: IEntity
{
public abstract object EntityID { get; set; }
}
public class Campaign : BaseEntity
{
#region Properties
public int ID { get; set; }
public string Name { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedOnDate { get; set; }
public virtual List<Fee> Fees { get; set; }
#endregion
#region BaseEntity Implementation
public override object EntityID
{
get { return this.ID; }
}
#endregion
#region Constructors
public Campaign()
{
this.CreatedOnDate = DateTime.Now;
this.Fees = new List<Fee>();
}
#endregion
}
//View model
//THIS is the class you want to validate
public class CampaignViewModel
{
#region Properties
public int ID { get; set; }
[StringLength(50)]
public string Name { get; set; }
[Required]
public string CreatedBy { get; set; }
public DateTime CreatedOnDate { get; set; }
public Fee AssociatedFee { get; set; }
#endregion
#region Constructors
public CampaignViewModel()
{ }
public CampaignViewModel(Campaign data)
{
this.ID = data.ID
this.Name = data.Name;
this.CreatedBy = data.CreatedBy;
this.CreatedOn = data.CreatedOn;
this.AssociatedFee = data.Fees.Where(x=>x.Active && x.ID == this.ID);
//Just an example
}
#endregion
}
Also, you could use Fluent Validation for a more in-depth separation of concerns. (http://fluentvalidation.codeplex.com/)
I was curious if it is possible to map an intermediate table through a containing object.
public class Subscriber : IEntity
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
private ChannelList _subscribedList { get; set; }
public int NumSubscribedChannels { get { return _subscribedList.Count(); } }
}
public class HelpChannel : IEntity
{
[Key]
public int Id { get; set; }
public string name { get; set; }
public string category { get; set; }
public int group { get; set; }
}
I need to have a subscriber table, channel table and an intermediate table to link a subscriber to his/her channels.
Is it possible to map the list that is within the ChannelList object to the Subscriber Model?
I figured that's probably not possible and that I would need to just have a private List for EF to map. But I wasn't sure if EF will do that for private variables. Will it?
I'm hoping that is does because if it has to be public to maintain the encapsulation.
You can map private properties in EF code-first. Here is a nice description how to do it. In your case it is about the mapping of Subscriber._subscribedList. What you can't do is this (in the context's override of OnModelCreating):
modelBuilder.Entity<Subscriber>().HasMany(x => x._subscribedList);
It won't compile, because _subscribedList is private.
What you can do is create a nested mapping class in Subscriber:
public class Subscriber : IEntity
{
...
private ICollection<HelpChannel> _subscribedList { get; set; } // ICollection!
public class SubscriberMapper : EntityTypeConfiguration<Subscriber>
{
public SubscriberMapper()
{
HasMany(s => s._subscribedList);
}
}
}
and in OnModelCreating:
modelBuilder.Configurations.Add(new Subscriber.SubscriberMapping());
You may want to make _subscribedList protected virtual, to allow lazy loading. But it is even possible to do eager loading with Include:
context.Subscribers.Include("_subscribedList");