In my project I have several classes with properties that should be unique, and I want to write a custom attribute that can be used on all the properties. For example I have class User with property username and class Application with property name that should be unique and I want to be able to do this:
[Unique(ErrorMessage = "Username alreadey exists")]
public string username {get;set;}
[Unique(ErrorMessage = "Name alreadey exists")]
public string name {get; set;}
Can someone please tell how to do this ?
Since properties are unique per context, you should verify uniqueness within the context's add functionality. In other words, this attribute should do nothing when you create a random instance of your object, but the collection you add it to should check for properties which have the Unique attribute and verify the new entity satisfies those constraints, or reject it.
Related
I have a POCO User that has a Company property.
When create a User in WebAPI I would like to just set the Company_Id that I am getting passed in json in the request and not have to load the Company entity from the database context in order to set it in the User.
I tried adding a User.Company_Id property in addition to the User.Company property and migrations ends up creating a Company_Id1 column in my database.
you should use Forign key attribute to let EF know it's a foreign key.
public class User
{
[ForeignKey("Company_Id")]
public Company Company {get;set;}
public int Company_Id {get;set;}
}
Given the Model:
Public Class Customer
Property Id() As Guid
Property FirstName() As String
Property MiddleName() As String
Property LastName() As String
Property Addresses() As ICollection(Of Address)
End Class
Public Class Address
Property Id() As Guid
Property Name() As String
Property Street() As String
Property City() As String
Property Zip() As String
Public Property Customer() As Customer
End Class
Entity Framework 6 Code First has created a column called Customer_Id in my table Addresses. Now, I'd like to add a Property Customer_Id to my class Address that represents the existing foreign key relation:
Public Class Address
Property Id() As Guid
Property Name() As String
Property Street() As String
Property City() As String
Property Zip() As String
Public Property Customer() As Customer
//Added
Public Property Customer_Id() As Guid
End Class
Unfortunately this results in an InvalidOperationException while creating the DbContext saying:
The model backing the 'DataContext' context has changed since the database was created.
I tried different property names (with and without underscore, different casing). But still no luck. So, what is the correct way to add those properties subsequently without the need for migrations? I assume it's possible, because the model does not really change, I am only changing from an implicit declaration of a property to an explicit...
Update:
The responses show me, that I did not explain the problem very well. After some more reading I found the correct names now: I have an application which is installed several times at customer locations (therefore dropping and recreating the database is no option). Currently, it depends on Entity Framework's Independent Associations, but I want to have the Foreign Key in my entity as well (this is no change to the model, the foreign key is already there, but does not exist as a property in my entity, since this is currently only relying on the IA instead). I did not manage to add it without EF thinking my Database is outdated.
for me two ways :
drop table __MigrationHistory : that is have the new model runs, but forget migration functionalities
create a new db by changing the connection string of the application. Replace old __MigrationHistory by __MigrationHistory of the newly created db
Never tested the second solution, but it should work.
Before using any solution:
backup you db.
Before using first solution: are you sure you will never need migration functionalities ?
This exception is because you change your model. You have to set migration strategy. Please look at:
http://msdn.microsoft.com/en-us/data/jj591621#enabling
(edited)
First of all you have to remove that exception. Even if you didn't add any new column to your database your model has changed because you added new property to Address class. If you check your DB you will find dbo.__MigrationHistory table with Model column. Last (earliest) value from that column is used for checking that your model and DB are compatible. I'm not sure but I think that EF stores there binary serialized model. So the solution is - recreate DB or add migration (probably empty migration).
(edited)
When you want to set FK you can do this very simple by Data Annotations
// c# example
public class Address
{
...
public string CustomerId { get; set; }
[ForeignKey("CustomerId")]
public Customer Customer { get; set; }
}
or in fluent api
// c# example
modelBuilder.Entity<Address>()
.HasRequired(arg => arg.Customer)
.WithMany()
.HasForeignKey(arg => arg.CustomerId);
or look at:
http://weblogs.asp.net/manavi/archive/2011/05/01/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations.aspx
http://msdn.microsoft.com/en-us/data/hh134698.aspx
I'm trying to set the Id property of my class as combination of another 2 properties:
public class Student
{
public string Id {get;set;}
public Guid StudentNumber {get;set;]
public string SchoolId {get;set;}
}
I want that StudentNumber and SchoolId to be the Id of the object when I save it.
How can it be done?
Have a look at the IdGenerator classes to custom create an id from the two properties.
http://docs.mongodb.org/ecosystem/tutorial/serialize-documents-with-the-csharp-driver/
You'll need to create a new IdGenerator class which implements the IIdGenerator interface (docs). There are two simple methods to implement.
Do note that a document's _id cannot be changed once saved. You'd need to resave it as a new document. So, if the SchoolId property changes, you may need to recreate the document.
Also, you might just consider creating a composite index for the two fields and leave the _id as an ObjectId.
A friend reported a problem with a computed column, Entity Framework, and Breeze
We have a table with a "FullName" column computed by the database. When creating a new Person, Breeze sends the FullName property value to the server, even though it’s not being set at all, and that triggers an error when trying to insert the new Person instance. The database throws this exception:
The column "FullName" cannot be modified because it is either a computed column or is the result of a UNION operator.
Here is the relevant portion of the SQL Table definition:
CREATE TABLE [dbo].[Person](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[FirstName] [varchar](100) NULL,
[MiddleName] [varchar](100) NULL,
[LastName] [varchar](100) NOT NULL,
[FullName] AS ((([Patient].[LastName]+',') + isnull(' '+[Patient].[FirstName],'')) + isnull(' '+[Patient].[MiddleName],'')),
...
My friend tells me the corresponding "Code First" class looks something like this:
public class Person {
public int ID {get; set;}
public string FirstName {get; set;}
public string MiddleName {get; set;}
public string LastName {get; set;}
public string FullName {get; set;}
...
}
The answer to this question explains the problem and offers a solution.
Design issues
Everyone looking at this wonders why there is a computed column for FullName and, secondarily, why this property is exposed to the client.
Let's just assume there is a good reason for the computed column, a good reason for the model to get the value from the table instead of calculating the value itself, and a good reason to send it to the client rather than have the client calculate it. Here's what he told me about that;
"We need to include the FullName in queries"
Life works out this way sometimes.
Consequences
Notice that the FullName property has a public setter. The EF metadata generator for the Person class cannot tell that this is a read-only property. FullName looks just like LastName. The metadata say "this is normal read/write property."
Breeze doesn't see a difference either. The client app may not touch this property, but Breeze has to send a value for it when creating a new Person. Back on the server, the Breeze EFContextProvider thinks it should pass that value along when creating the EF entity. The stage is set for disaster.
What can you do if (a) you can't change the table and (b) you can't change the model's FullName property definition?
A Solution
EF needs your help. You should tell EF that this is actually a database computed property. You could use the EF fluent interface or use the attribute as shown here:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public String FullName { get; set; }
Add this attribute and EF knows this property is read-only. It will generate the appropriate metadata and you can save a new Person cleanly. Omit it and you'll get the exception.
Note that this is only necessary for Code First. If he'd generated the model Database First, EF knows that the column is computed and doesn’t try to set it.
Be aware of a similar issue with store-generated keys. The default for an integer key is "store-generated" but the default for a Guid key is "client generated". If, in your table, the database actually sets the Guid, you must mark the ID property with [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
I have been reading some artices about using the RelationshipManager to gain access to the entries that have related data. It is still unclear to me what the best way to audit when an entity whose related data is added or updated.
Sample Classes:
public class Rfi
{
public Guid Id {get;set;}
public string Number {get;set;}
public virtual ICollection<Attachment> Attachments {get;set;}
}
public Class Attachment
{
public Guid Id {get;set;}
public string Name {get;set;}
public string Description {get;set;}
public string FileName {get;set;}
public string Path {get;set;}
}
Sample Mappings:
public class RfiMapping: EntityTypeConfiguration<Rfi>
{
public Rfimapping()
{
HasMany(r => r.Attachments).WithMany().Map(m =>
{
m.MapLeftKey("RfiId");
m.MapRightKey("AttachmentId");
m.ToTable("Rfi_Attachments");
});
}
}
I am using the Repository and Unit Of Work patterns. My UoW inherits from DbContext. A repository call may look like this:
public void AddAttachmentToRfi(Attachment attachment, Guid rfiId)
{
var rfi = _rfiRepository.FindById(rfiId);
rfi.Attachments.Add(attachment);
_rfiRepository.UnitOfWork.Commit();
}
Is it possible , in an overridden SaveChanges method, to figure out that an Attachment entity was added to an Rfi entity? When I traverse the, say ChangeTracker.Entries, I am not seeing its state being set to modified. Which makes sense, because I am only adding to the relationships and not the entity directly.
I know to cast my DbContext to an IObjectContextAdapter, but I am not sure what I need to do with the RelationshipManager to get the changes made to any of the relationships. I am also curious to know if I were to update an Attachment's Description property later on, if I can still see what changes were made to any related data.
My goal with this is, the user interface for the Rfi allows users to attach files (Rfi is obviously not the only entity that can have attachments). I need to show a history of everything that happens to an Rfi. This means if an attachment is added I need to audit it. If the attachment's data is updated, I need to audit those changes and show that they were updated via the Rfi interface. This may get complicated if that attachment is shared with another entity, but I will cross that road later.
As you say you are not changing any of the entities only the relationship between them.
EF will then convert this to an insert into or delete from the Rfi_Attachments table.
One way to audit this is to add a database trigger that writes an entry to a log table, each time an entry is added or deleted.