Entity Framework - inheriting from model - c#

I'm new to the Entity Framework and I've followed tutorials online to create my SQL Server database and made several models and a context class to include properties to access them.
This is my account model:
public class Account
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
This is my context class:
public class DashContext : DbContext
{
public DashContext()
: base(Constants.ConnectionString)
{
this.Configuration.LazyLoadingEnabled = true;
this.Configuration.ProxyCreationEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<DashContext>(null);
}
public DbSet<Account> Accounts { get; set; }
}
This works - when I access the DbSet property I can access all the account entires in my database.
However, I want to create an implmentation of the Account class that contains more properties than just columns because it has to interact with my program.
So, I tried to do the following:
public class GameAccount : Account
{
public int SomeSpecialProperty { get; set; }
}
However, when I'm using my context class to get the Account object, I'm not sure how to convert it to GameAccount. I know I can create a constructor that copies the properties from Account to GameAccount, like this:
public class GameAccount
{
public int ID { get; private set; }
public string Username { get; private set; }
public string Password { get; private set; }
public GameAccount(Account model)
{
this.ID = model.ID;
this.Username = model.Username;
this.Password = model.Password;
}
}
...but that seems a bit inefficent to me and I'm sure there's a simpler way.
What do you think?

You have a few options:
Option 1
Use a partial class as indicated by Fruchtzwerg.
Option 2
You can use AutoMapper to map the items from one type to the other. Here is an example:
// Notice the ReverseMap call. That will allow mapping in both directions.
Mapper.Initialize(cfg =>
cfg.CreateMap<Account, GameAccount>().ReverseMap());
var account = Mapper.Map<Account>(new GameAccount());
var gameAccount = Mapper.Map<GameAccount>(account);

Copy Constructors could be very costly to develop and maintenance. Typically generated classes of the Entity Framework are partial.
BradleyDotNET explains:
When code is generated; you don't want your additional methods/properties/whatever blown away, so the designers mark such classes partial to allow users to put additional code in a different file.
So a possible approach is extending the class
public partial class Account
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
with additional properties like
public partial class Account
{
public int SomeSpecialProperty { get; set; }
}

Related

ADO.NET Entity Model - Dynamically assigning TableAttribute

I have a need for a project to allow the user to setup database info and table names in the config file. I want to use ADO.NET Entity Model to use the LINQ and just stay away from SQL the most I can to make it easier on myself. Is there a way to dynamically assign what table a Class needs to access for the modal?
For example:
This is what it looks like normally
[Table("database.table")]
public partial class table
{
[Key]
[Column(TypeName = "usmallint")]
public int ID { get; set; }
[Required]
[StringLength(128)]
public string Instance { get; set; }
[Required]
[StringLength(60)]
public string Name { get; set; }
}
I want to be able to dynamically set the TableAttribute so the model knows what table to access for the class.
[Table(Config.DBName + Config.tableName)]
public partial class table
{
[Key]
[Column(TypeName = "usmallint")]
public int ID { get; set; }
[Required]
[StringLength(128)]
public string Instance { get; set; }
[Required]
[StringLength(60)]
public string Name { get; set; }
}
Any help or letting me know that it is not possible would be appreciated.
I've not tested this, but I think you can do this via implementing custom conventions - if you're using EF6 at least.
First, you need to create a custom Convention:
public class CustomTableNameConvention : IStoreModelConvention<EntitySet>
{
private readonly string _tablePrefix;
public CustomTableNameConvention(string tablePrefix)
{
_tablePrefix = tablePrefix;
}
public void Apply(EntitySet item, DbModel model)
{
//change table name.
item.Table = $"{_tablePrefix}" + item.Table;
}
}
Next, you need to add this convention in the OnModelCreating method of your Context:
public class MyContext : DbContext
{
public MyContext(string connectionString) : base(connectionstring)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//get the dynamic table prefix...
var myAppPrefix = "user1";
modelBuilder.Conventions.Add(new CustomTableNameConvention(myAppPrefix));
base.OnModelCreating(modelBuilder);
}
public DbSet<SomeModel> { get; set; }
...
}
... Then, whenever the model in this application instance starts up, it should run through the above when deciding what the table name(s) should be.
Just replace the myAppPrefix = ... code with a call to an appropriate service to get the prefix for this instance.
the obvious caveat with this is that you cannot use a value for the prefix which is returned from the database (at least, not via this Context), as the Context isn't yet initialised.. so you'd have to either store it in settings or pass it in some other way.
Hope this helps.

How to Specify Entity Framework Core Table Mapping?

I've made a simple Entity Framework ASP Core Application that works but I do not know why:
I've made a context like this:
public class AstootContext : DbContext
{
public AstootContext(DbContextOptions<AstootContext> options)
: base(options)
{ }
public DbSet<Account> Accounts { get; set; }
public DbSet<User> Users { get; set; }
}
And I have two tables with models like this:
public class Account
{
public int Id { get; set; }
public string Username { get; set; }
public string PasswordHash { get; set; }
public DateTime Created { get; set; }
List<User> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public DateTime Birthday { get; set; }
public Account Account { get; set; }
}
The interesting thing is that when I run my application it actually can pick up the data. It just seems weird because I have not specified any table mapping.
I'm assuming this just automaps because the specified tables are the same name.
My questions are:
How do I specify Table explicit table mapping in case I do not want my model names to be exactly the same as the DB?
How do I specify Custom Column Mapping.
Is there anything special I have to specify for Primary/Foreign Keys
edit
To clarify
Say I had a table in the DB MyAccounts and I wanted to map that to an entity Accounts.
Say I had a column password and I wanted that to map to a POCO property PasswordHash
To specify the name of the database table, you can use an attribute or the fluent API:
Using Attributes:
[Table("MyAccountsTable")]
public class Account
{
public string PasswordHash { get; set; }
}
Using Fluent API:
public class YourContext : DbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Language>(entity => {
entity.ToTable("MyAccountsTable");
});
}
}
To name your columns manually, it's very similar and you can use an attribute or the fluent API:
Using Attributes:
public class Account
{
[Column("MyPasswordHashColumn")]
public string PasswordHash { get; set; }
}
Using Fluent API:
public class YourContext : DbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Language>(x => x
.ToTable("MyAccountsTable")
.Property(entity => entity.PasswordHash)
.HasColumnName("MyPasswordHashColumn")
);
}
}

ASP MVC5 Identity User Abstraction

I want to build N-tire web application with default Identity 2 provider. So, my Data layer contains pure c# classes with model definition, without any externad dependency. But it is impossible to link some classes to my Application User without adding AspNet.Identity reference.
I have tried to make an interface of User class:
public interface ISystemUser
{
string Id { get; set; }
string Title { get; set; }
}
public class Place
{
public int Id { get; set; }
public string Address { get; set; }
public ISystemUser User { get; set; }
}
And in Infrastructure layer substitute it with implementation:
public class ApplicationUser : IdentityUser, ISystemUser
{
public string Title { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("DefaultConnection", throwIfV1Schema: false)
{
}
public DbSet<Place> Places { get; set; }
}
But entity framework does not creates relation between entities.
Is there any 'right' way do implement this or it is necesarry to add reference?
There's a workaround, which is pretty ugly in my opinion but works.
You will need 2 classes, one for the User and another for the ApplicationUser. ApplicationUser must have all properties of User. Like this:
//Domain Layer
public class User
{
public string Id { get; set; }
public string Title { get; set; }
}
//Infrastructure Layer
public class ApplicationUser
{
public string Title { get; set; }
}
Now, the trick is mapping the User class to the same table of the ApplicationUser class. Like this:
public class UserConfig : EntityTypeConfiguration<User>
{
public UserConfig()
{
HasKey(u => u.Id);
ToTable("AspNetUsers");
}
}
Hope it helps!

EF6 Code First and mapping via annotations, how do I build the configuration?

I was using EF6's fluent mapping like this:
public SomeClass
{
public int SomeID { get; set; }
}
public SomeClassMap : EntityTypeConfiguration<SomeClass>
{
public SomeClassMap()
{
ToTable("SomeTable");
HasKey(c => c.SomeID);
}
}
And building the configuration from the assembly of the first requested type (model):
public class MyContext : DbContext
{
private Assembly _assembly;
public MyContext(string connectionName, Type type)
{
//checks
Database.Connection.ConnectionString = ConfigurationManager.ConnectionStrings[connectionName].ConnectionString;
_assembly = Assembly.GetAssembly(type);
}
public override void OnModelCreating(DbModelBuilder modelBuilder)
{
//conventions
//Not the ideal solution, still looking for something better
modelBuilder.Configurations.AddFromAssembly(_assembly);
}
}
Now I want to make a generic Data project, independent of the models, so I'd like to map via annotations and simply call the generic methods in my Data project.
I've mapped the class:
[Table("SomeTable")]
public SomeClass
{
[Key]
public int SomeID { get; set; }
}
Now how do I pass this to the Data project so it can build the model configuration?
Edit This might be relevant, since my Data project is generic, I don't have the DbSet<Entity> variables in it, instead I'm calling the context.Set<Entity> and using the functions from there.
You can create a DbContext by the constructor that takes a DbCompiledModel.
You can build the compiled model separately. It's up to you where you want to implement that responsibility, but the model classes should be in scope.
Here's an example:
Some classes:
[Table("Company")]
class Company
{
[Key]
public int CompanyID { get; set; }
public string Name { get; set; }
public virtual ICollection<Location> Locations { get; set; }
}
[Table("Location")]
class Location
{
[Key]
public int LocationID { get; set; }
public string Name { get; set; }
[ForeignKey("Company")]
public int CompanyID { get; set; }
public virtual Company Company { get; set; }
}
And the creation + usage of a DbContext:
// Create a model + register types to it.
var mb = new DbModelBuilder();
mb.Entity<Company>();
mb.Entity<Location>();
// Or:
//mb.RegisterEntityType(typeof(Company));
//mb.RegisterEntityType(typeof(Location));
// Build and compile the model
var connString = #"server=myServer;database=theDataBase;Integrated Security=SSPI;MultipleActiveResultSets=True";
var dbModel = mb.Build(new SqlConnection(connString));
var compiledModel = dbModel.Compile();
// Create a DbContext using the compiled model.
var db = new DbContext(connString, compiledModel);
Database.SetInitializer<DbContext>(null); // Prevent creation of migration table
// Ready to go!
var companies = db.Set<Company>().Include(c => c.Locations).ToList();
You can make this more efficient by storing and reusing cached DbCompiledModels.

MVC 4 EF database first Model constructor updates

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/)

Categories