Problems creating my own Custom DataAttribute for LinqToSql - linq-to-sql

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

Related

Adding columns to joint tables with payload in MVC

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.
--

Can I specify a discriminator column with a table-per-type mapping?

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();
}
}

Entity Framework/Linq to sql model to business model

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!

PLINQO / LINQ-To-SQL - Generated Entity Self Save Method?

Hi I'm trying to create a basic data model / layer
The idea is to have:
Task task = TaskRepository.GetTask(2);
task.Description = "The task has changed";
task.Save();
Is this possible? I've tried the code below
Note: The TaskRepository.GetTask() methods detaches the Task entity.
I'd expect this to work, any ideas why it doesnt?
Thanks
public partial class Task
{
// Place custom code here.
public void Save()
{
using (TinyTaskDataContext db = new TinyTaskDataContext { Log = Console.Out })
{
db.Task.Attach(this);
db.SubmitChanges();
}
}
#region Metadata
// For more information about how to use the metadata class visit:
// http://www.plinqo.com/metadata.ashx
[CodeSmith.Data.Audit.Audit]
internal class Metadata
{
// WARNING: Only attributes inside of this class will be preserved.
public int TaskId { get; set; }
[Required]
public string Name { get; set; }
[Now(EntityState.New)]
[CodeSmith.Data.Audit.NotAudited]
public System.DateTime DateCreated { get; set; }
}
#endregion
}
Having done some reading I've realised I was implmenting the Repository pattern incorrectly. I should have been adding the Save method to the repository for conventions sake.
However, the actually problem I was having with regard to commiting the disconnected dataset was due to optimistic concurrency. The datacontext's job is to keep track of the state of it's entities. When entities become disconnected you loose that state.
I've found you need to add a timestamp field to the database table or I can set the UpdateCheck field on each column in my dbml file.
Here is some info about the UpdateCheck
Some useful links about disconnected Linq and plinqo
Great info on implementing the Repository pattern with LINQ
Short tutorial for implementing for updating and reattaching entities
Previously answer question
Rick Strahl on LINQ to SQL and attaching Entities
There is no need for this line (Task task = new Task();). The above should work although I've never seen it implemented in this manner. Have you thought about using the managers? Are you running into any runtime errors?
Thanks
-Blake Niemyjski

How to prevent linq-to-sql designer undo my changing

Thanks for your attention in advance,
I’ve met an issue with LINQ-2-SQL designer in VS 2008 SP1 which has made me CRAZY. I use Linq2sql as my DAL. It seems Linq2sql speeds up coding in the first step but lots of issues arise in feature specifically with table or object inheritance.
In this case I have a class Entity that all other entity classes generated by Linq2sql designer inherit from.
public abstract class Entity
{
public virtual Guid ID { get; protected set; }
}
public partial class User : monius.Data.Entity
{
}
And the following generated by L2S designer (DataModel.designer.cs)
[Column(Storage = "_ID", AutoSync = AutoSync.OnInsert, DbType = "UniqueIdentifier NOT NULL",
IsPrimaryKey = true, IsDbGenerated = true, UpdateCheck = UpdateCheck.Never)]
[DataMember(Order = 1)]
public System.Guid ID
{
get
{
return this._ID;
}
set
{
if ((this._ID != value))
{
this.OnIDChanging(value);
this.SendPropertyChanging();
this._ID = value;
this.SendPropertyChanged("ID");
this.OnIDChanged();
}
}
}
When I compile the code VS warns me that
Warning 1 'User.ID' hides inherited member 'Entity.ID'. To make the current member override that mplementation, add the override keyword. Otherwise add the new keyword.
That warning is obvious and I have to change the code generated by L2S designer (DataModel.designer.cs) to
[…]
public override System.Guid ID
{
…
protected set
…
}
And the code compiled with no error or warning and everyone is happy. But that is not the end of story.
As soon as I made changes to entities of the diagram (dbml) or even I open dbml file to view it, any change manually I made to designer has been vanished and POOF! Redo AGAIN. That is a painful job.
Now I wonder if there is a way to force L2S designer not changing portions of auto-generated code.
I’ll be appreciated if someone kindly helps me with this issue.
I suppose the questioner by now has overcome the issue. But I'll add an answer , for the benefiit of others who, like myself, googled their way to this post.
If you need to an override modifier on your linqToSql generated property;
1) open the dbml
2) right click and select properties on the Property you wish to add the override (or virtual, new or new virtual) keyword
3) change the Inheritance Modifier property to what you desire.
If all your base class does is:
public abstract class Entity
{
public virtual Guid ID { get; protected set; }
}
...then why not make it an interface instead?
Alternatively, you may want to look into using Damien Guard's T4 templates to customize the output of the code generator: http://l2st4.codeplex.com/