Evening, I have recently gotten help making my database in MVC on SO (I'm very thankful for that). I have another question, as I'm not in graduate school yet so I don't know the best-practice, I thought someone might in my case.
I am making joint tables between two classes, a User table and a Course table. Put simply, a User can have a Course, and this is being made in my OnModelCreating method of my DbContext class
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new UserMap());
base.OnModelCreating(modelBuilder);
}
<-- snip --> Below is my class for the mappings <-- snip -->
public class UserMap : EntityTypeConfiguration<ApplicationUser>
{
public UserMap()
{
// Primary Key
this.HasKey(m => m.Id);
// UserHasCourse
this.HasMany(m => m.Courses)
.WithMany()
.Map(n =>
{
n.MapLeftKey("UserId");
n.MapRightKey("CourseId");
n.ToTable("UserHasCourse");
});
}
}
What I want to do is be able to add more columns to the UserHasCourse. The question is, how do I do this if the table is being generated here? This will not be the only case where I need to add a column/s that isn't necessarily related to the model (ie. I want to add the columns Credits and Grade, corresponding to the number of credits a user has earned in the course with a grade). I don't want to have Credits and Grade saved in the Courses table (as they only need to be in this joint table).
I can always add in two columns in the Server Explorer, but is there a way I should do this in code (if it is better that way)?
Entity Framework can do LOTS of stuff under the covers. However, this does not necessarily mean it's a good idea to let EF "figure stuff out" for you. I use EF all the time and find the CodeFirst approach a great way to get what you want done. What I would suggest is more of a declarative approach. Tell EF exactly what you want to do.
//Models
public class User
{
public int UserId {get;set;}
public int CourseId {get;set;}
public virtual Course Course {get;set;}
}
public class Course
{
private ICollection<User> _users;
public int CourseId {get;set;}
public string CustomPropOne {get;set;}
public string CustomPropTwo {get;set;}
// mark this as virtual if you want to enable lazy loading
public ICollection<User> Users
{
get{ return _users ?? ( _users = new List<User>()); }
protected set{ _users = value;}
}
}
//Mapping Classes
public class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
HasKey(u => u.UserId);
HasRequired(u => u.Course)
.WithMany(c => c.Users)
.HasForeignKey(u => u.CourseId);
}
}
//etc.
By declaring EXACTLY what you want to happen, you ensure your db is setup correctly and it becomes trivial to change/update your db model.
--
Related
I'm working on developing my first Data Driven Domain using Dependency Injection in ASP.net.
In my Data Access Layer if have created some domain data models, for example:
public class Company {
public Guid CompanyId { get; set; }
public string Name { get; set; }
}
public class Employee {
public Guid EmployeeId { get; set; }
public Guid CompanyId { get; set; }
public string Name { get; set; }
}
I have then developed an interface such as:
public interface ICompanyService {
IEnumerable<Model.Company> GetCompanies();
IEnumerable<Model.Employee> GetEmployees();
IEnumerable<Model.Employee> GetEmployees(Guid companyId);
}
In a separate module I have implemented this interface using Linq to Sql:
public class CompanyService : ICompanyService {
public IEnumerable<Model.Employee> GetEmployees();
{
return EmployeeDb
.OrderBy(e => e.Name)
.Select(e => e.ToDomainEntity())
.AsEnumerable();
}
}
Where ToDomainEntity() is implemented in the employee repository class as an extension method to the base entity class:
public Model.EmployeeToDomainEntity()
{
return new Model.Employee {
EmployeeId = this.EmployeeId,
CompanyId = this.CompanyId,
Name = this.Name
};
}
To this point, I have more or less followed the patterns as described in Mark Seeman's excellent book 'Dependency Injection in .NET' - and all works nicely.
I would like however to extend my basic models to also include key reference models, so the domain Employee class would become:
public class Employee {
public Guid EmployeeId { get; set; }
public Guid CompanyId { get; set; }
public Company { get; set; }
public string Name { get; set; }
}
and the ToDomainEntity() function would be extended to:
public Model.Employee ToDomainEntity()
{
return new Model.Employee {
EmployeeId = this.EmployeeId,
CompanyId = this.CompanyId,
Company = (this.Company == null) ? null : this.Company.ToDomainEntity()
Name = this.Name
};
}
I suspect that this might be 'bad practice' from a domain modelling point of view, but the problem I have encountered would also, I think, hold true if I were to develop a specific View Model to achieve the same purpose.
In essence, the problem I have run into is the speed/efficiency of populating the data models. If I use the ToDomainEntity() approach described above, Linq to Sql creates a separate SQL call to retrieve the data for each Employee's Company record. This, as you would expect, increases the time taken to evaluate the SQL expression quite considerably (from around 100ms to 7 seconds on our test database), particularly if the data tree is complex (as separate SQL calls are made to populate each node/sub-node of the tree).
If I create the data model 'inline...
public IEnumerable<Model.Employee> GetEmployees();
{
return EmployeeDb
.OrderBy(e => e.Name)
.Select(e => new Model.Employee {
EmployeeId = e.EmployeeId,
/* Other field mappings */
Company = new Model.Company {
CompanyId = e.Company.CompanyId,
/* Other field mappings */
}
}).AsEnumerable();
}
Linq to SQL produces a nice, tight SQL statement that natively uses the 'inner join' method to associate the Company with the Employee.
I have two questions:
1) Is it considered 'bad practice' to reference associated data classes from within a domain class object?
2) If this is the case, and a specific View Model is created for the purpose, what is the right way of populating the model using without having to resort to creating inline assignment blocks to build the expression tree?
Any help/advice would be much appreciated.
The problem is caused by having both data layer entities and domain layer entities and needing a mapping between the two. Although you can get this to work, this makes everything very complex, as you are already experiencing. You are making mappings between data and domain, and will soon add many more mappings for these same entities, because of performance reasons and because other business logic and presentation logic will need different data.
The only real solution is to ditch your data entities and create POCO model objects that can directly be serialized to your backend store (SQL server).
POCO entities is something that is supported in LINQ to SQL from day one, but I think it would be better to migrate to Entity Framework Code First.
When doing this, you can expose IQueryable<T> interfaces from your repositories (you currently called your repository ICompanyService, but a better name would be ICompanyRepository). This allows you to do efficient LINQ queries. When querying directly over a query provider you can prevent loading complete entities. For instance:
from employee in this.repository.GetEmployees()
where employee.Company.Name.StartWith(searchString)
select new
{
employee.Name,
employee.Company.Location
};
When working with IQueryable<T>, LINQ to SQL and Entity Framework will translate this to a very efficient SQL query that only returns the employe name and company location from the database with filtering inside the database (compared to do filtering in your .NET application when GetEmployees() returns an IEnumerable<T>).
You can ask Linq2Sql to preload certain entities (as opposed to lazy load them) using DataLoadOptions.LoadWith method see: http://msdn.microsoft.com/en-us/library/bb534268.aspx.
If you do this with the Company entity then I think Linq2Sql won't have to reach to the database to fetch it again.
I'm new to EF, EF Code First, and EF with MySQL. When would EF Code First create your tables within a ASP.NET MVC web project?
I created a Person model. Then generated the Controller and standard Views.
When I hit the Index method of the Person controller it tries to pull back a list of all People. Then I get the error:
An error occurred while executing the command definition. See the inner exception for details.
The inner exception:
Table 'testmvc.people' doesn't exist
So I've made it past the connection. But the table wasn't created. How do I create the tables? Also how do I prevent the pluralization of Person to People in the naming scheme?
The simplest way to generate the database schema (people table and others) is to set a database initializing strategy like this:
Database.SetInitializer<SomeContext>( new
DropCreateDatabaseAlways<SomeContext>());
This code needs to run before you attempt to load any data, so the Application_Start() method in Global.asax would be a good place to do that. There are several ways to initialize, so you may want to take a look at them before choosing one, see http://msdn.microsoft.com/en-us/library/system.data.entity%28v=vs.103%29.aspx and look at the methods that implement IDatabaseInitializer. Officially, there is a strategy by default, although I have never quite found that to work for me.
You should also be aware that while this method is great for prototyping and development, you can't quite use it on production database with live data since the database is first dropped and then recreated. There are other methods of doing this at that point - see Database migrations for Entity Framework 4 for possibilities.
Regarding your other question of using non-pluralized table names, there are several ways to do this. One way is to annotate the Person class like this:
[Table("Person")]
class Person
{
// some field attributes
}
To set this for all tables at once, you can use the fluent API, like this:
class SomeContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
MySql with entity framework needs some little tweaks. You need to create three classes(you can check https://learn.microsoft.com/en-us/aspnet/identity/overview/getting-started/aspnet-identity-using-mysql-storage-with-an-entityframework-mysql-provider for more details). First create a MySqlHistoryContext class.
public class MySqlHistoryContext : HistoryContext
{
public MySqlHistoryContext(
DbConnection existingConnection,
string defaultSchema)
: base(existingConnection, defaultSchema)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<HistoryRow>().Property(h =>
h.MigrationId).HasMaxLength(100).IsRequired();
modelBuilder.Entity<HistoryRow>().Property(h =>
h.ContextKey).HasMaxLength(200).IsRequired();
}
}
Create a MySqlConfiguration class next
public class MySqlConfiguration : DbConfiguration
{
public MySqlConfiguration()
{
SetHistoryContext(
"MySql.Data.MySqlClient", (conn, schema) => new MySqlHistoryContext(conn, schema));
}
}
Create MySqlInitializer class next
public class MySqlInitializer : IDatabaseInitializer<ApplicationDbContext>
{
public void InitializeDatabase(ApplicationDbContext context)
{
if (!context.Database.Exists())
{
// if database did not exist before - create it
context.Database.Create();
}
else
{
// query to check if MigrationHistory table is present in the database
var migrationHistoryTableExists =
((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery<int>(
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema =
'IdentityMySQLDatabase' AND table_name = '__MigrationHistory'");
// if MigrationHistory table is not there (which is the case first time
we run) - create it
if (migrationHistoryTableExists.FirstOrDefault() == 0)
{
context.Database.Delete();
context.Database.Create();
}
}
}
}
Open the IdentityModels.cs in the model folder.Add this to the ApplicationDbContext : IdentityDbContext class
static ApplicationDbContext()
{
Database.SetInitializer(new MySqlInitializer());
}
I have a class hierarchy that I want to map across several tables using Entity Framework 4.1 Code First. It's like table-per-type (TPT) but I also want a discrimator column.
The hierarchy looks something like:
public class Event
{
public Guid Id { get; set; }
public string Code { get; set; } // discriminator
public DateTime Date { get; set; }
}
public class Party : Event
{
public int AttendeeCount { get; set; }
}
public class BirthdayParty : Party
{
public int Age { get; set; }
}
public class WeddingParty : Party
{
public string Surname { get; set; }
}
This is a pretty weak example but I hope it makes sense. There'll be an "Events" table, a "Parties" table and a table for each kind of party. However, the discriminator column ("Code") will have a known value for each kind of event, like "BIRTH" for birthday parties or "WEDDING" for wedding parties.
The idea is that if I query for just birthday parties on a given date, EF would know to add Code = 'BIRTH' to my query instead of doing a bunch of UNIONs and JOINs to work out which rows it needs.
I map my lowest-level classes like this:
var bd = modelBuilder.Entity<BirthdayParty>();
bd.ToTable("BirthdayParties");
bd.Property(p => p.Age).HasColumnName("BirthdayAge");
I now need to specify the discriminator value in there somehow. I've tried this:
modelBuilder.Entity<Event>().Map<BirthdayParty>(cfg =>
{
cfg.Requires("Code").HasValue("BIRTH");
});
... but that complains that I haven't specified the table name inside the call to Map. So I tried moving the ToTable call into there:
var bd = modelBuilder.Entity<BirthdayParty>();
bd.Property(p => p.Age).HasColumnName("BirthdayAge");
modelBuilder.Entity<Event>().Map<BirthdayParty>(cfg =>
{
cfg.Requires("Code").HasValue("BIRTH");
cfg.ToTable("BirthdayParties");
});
... and now it thinks I want a "Code" column in the "BirthdayParties" table, which is not correct. I've already told it that the "Code" column is in the "Events" table.
Is this even possible? Can I combine the use of a discriminator column with a table-per-type mapping?
Unfortunately this is not supported. Discriminator column can be used only in TPH. TPT differs entity types by mapped tables and it always produces those terrible queries. It could be nice feature so perhaps suggestion on Data UserVoice would make it implemented one day.
Update
There is already a suggestion on user voice for this titled "Discriminator column support in TPT inheritance".
I did an override on SaveChanges to accomplish something similar. I simply added an attribute onto the abstract class called Descriminator and set it based on the Concrete Class Name anytime something new is added.
public class MyContext : DbContext
{
public override int SaveChanges()
{
foreach (var item in ChangeTracker.Entries().Where(x=>x.Entity is MyAbstractClass && x.State == EntityState.Added))
{
((MyAbstractClass)item.Entity).Descriminator = item.Entity.GetType().Name;
}
return base.SaveChanges();
}
}
I'm coming from a stored procedure and creating the data access layer manually approach. I am trying to understand where I should fit Linq To SQL or entity frameworks into my normal planning. I normally seperate out the business layer from the DAL layer and use a repository inbetween.
It seems that people will either use the generated classes from linq to sql, extend them by using the partial class or do a full seperation and map the generated linq classes to seperate business entities. I am partial to the seperate Business entities. However, this seems to be counterintuitive.
One of my last projects used DDD and the entity framework. When needing to udpate an object it moved the business entity to the repistory layer which when going to the DAL layer would create a context and than requery the object. It would than update the values and resbumit.
I didn't see the large point as the data context wasn't saved and required an extra query to grab the object before updating. Normally I would just do the update(If concurrency wasn't an issue)
So my questions come down to:
Does it make sense to seperate linq to sql generated classes into Business entities?
Should the data context be saved or is that impractical?
Thanks for your time, trying to make sure I understand. I normally like to seperate out as it makes it cleaner to understand even in some smaller porjects.
I currently hand roll my own Dto classes and Datacontext instead of using auto-generated code files from Linq to Sql. To give some background of my solution architecture/modeling, I have a "Contract" project, and a "Dal" project. (Also a "Model" project, but I'll try to stay focused here on Dal only). Hand-rolling my own Dtos and Datacontext, makes everything a lot smaller and simpler, I'll give a few examples of how I do that here.
I never return out a Dto object outside of the Dal, in fact I make sure to declare them as internal. The way I return them out is I cast them as an interface (interfaces are located in my "Contract" layer). We'll make a simple "PersonRepository" that implements an "IPersonRetriever and IPersonSaver" interfaces.
Contracts:
public interface IPersonRetriever
{
IPerson GetPersonById(Guid personId);
}
public interface IPersonSaver
{
void SavePerson(IPerson person);
}
Dal:
public class PersonRepository : IPersonSaver, IPersonRetriever
{
private string _connectionString;
public PersonRepository(string connectionString)
{
_connectionString = connectionString;
}
IPerson IPersonRetriever.GetPersonById(Guid id)
{
using (var dc = new PersonDataContext(_connectionString))
{
return dc.PersonDtos.FirstOrDefault(p => p.PersonId == id);
}
}
void IPersonSaver.SavePerson(IPerson person)
{
using (var dc = new PersonDataContext(_connectionString))
{
var personDto = new PersonDto
{
Id = person.Id,
FirstName = person.FirstName,
Age = person.Age
};
dc.PersonDtos.InsertOnSubmit(personDto);
dc.SubmitChanges();
}
}
}
PersonDataContext:
internal class PersonDataContext : System.Data.Linq.DataContext
{
static MappingSource _mappingSource = new AttributeMappingSource(); // necessary for pre-compiled linq queries in .Net 4.0+
internal PersonDataContext(string connectionString) : base(connectionString, _mappingSource) { }
internal Table<PersonDto> PersonDtos { get { return GetTable<PersonDto>(); } }
}
[Table(Name = "dbo.Persons")]
internal class PersonDto : IPerson
{
[Column(Name = "PersonIdentityId", IsPrimaryKey = true, IsDbGenerated = false)]
internal Guid Id { get; set; }
[Column]
internal string FirstName { get; set; }
[Column]
internal int Age { get; set; }
#region IPerson implementation
Guid IPerson.Id { get { return this.Id; } }
string IPerson.FirstName { get { return this.FirstName; } }
int IPerson.Age { get { return this.Age; } }
#endregion
}
You will need to add the "Column" attribute to all of your Dto properties, but if you notice, if there is a one-to-one correlation between what you want the field to be exposed as on the interface, and the name of the actual table column, you won't need to add any of the Named Parameters. In this example my PersonId in the database is stored as "PersonIdentityId", yet I only want my interface to make the field say "Id".
That's how I do my Dal layer, I believe this layer should be dumb, real dumb. Dumb in the sense that it is only there for CRUD (Create, Retrieve, Update and Delete) operations. All of the business logic would go into my "Model" project, which would consume and utilize the IPersonSaver and IPersonRetriever interfaces.
Hope this helps!
I'm building LINQ Models by hand, because I want to (understand what's reall happening).
There is a great light weight tutorial on turning standard classes into Linq Models I am reading Here.
For my sample application I have created some models that look like:
public class LoginModel
{
[Column(IsPrimaryKey=true,
DbType="UniqueIdentifier NOT NULL",
CanBeNull=false)]
public Guid LoginID { get; set; }
// .. and more question useless properties...
}
I'm definitely seeing a pattern for the primary key which led me to creating...
[AttributeUsage(AttributeTargets.Property
| AttributeTargets.Field,
AllowMultiple = false)]
public sealed class ColumnPrimaryKeyAttribute : DataAttribute
{
public ColumnPrimaryKeyAttribute()
{
CanBeNull = false;
IsPrimaryKey = true;
DbType = "UniqueIdentifier NOT NULL";
}
// etc, etc...
}
So when I use my new Attribute, LINQ is not picking up my attribute (even though it inherits from the same DataAttribute as Column. Is there a step I'm missing, or should I abandon this idea?
Try inheriting from ColumnAttribute...
public class ColumnPrimaryKeyAttribute : ColumnAttribute
Edit:
Never mind, I see that ColumnAttribute is sealed. You may be out of luck as my guess is LINQ is doing a System.Attribute.GetCustomAttributes(typeof(ColumnAttribute));