I'm using .NET 3.5 SP1. I have entity 'AppUser':
public class AppUser : System.Data.Objects.DataClasses.EntityObject{
public int Uid {get; set;}
public string UserName {get; set;}
public string Password {get; set;}
public DateTime LastLogin {get; set;}
public string Name {get; set;}
public string Address {get; set;}
public string Comment {get; set;}
...........
}
To update ALL fields of an attached enitity:
public void Update(AppUser updateUser) {
AppUser user = ctx.AppUserSet.Where(u => u.UserId == userId).FirstOrDefault();
//This will update ALL fields
ctx.ApplyPropertyChanges(user.EntityKey.EntitySetName, updateUser);
ctx.SaveChanges();
}
I want to update all fields except Password and LastLogin. I can update individual fields, but will be cumbersome for entities with large number of fields.
Please tell, what is the best way to achive this?
Thank You.
Change the setters for those two properties to private in the EDMX/model.
I don't think that you can do this directly with EF without extra programming effort.
There are three possibilities:
Update through a view
Update using a stored procedure
Change your data model, such that the password and lastlogin are in a seperate table with a one to one relationship.
All of these require extra programming effort.
Related
I have a Channel domain model as:
public class Channel
{
public string Id {get; set;}
public List<ChannelUser> ChannelUsers {get; set;}
}
and a ChannelUser class as follows (I have additional meta data on the table, removed here for brevity, which is why I used a specfic class to represent the many-to-many relationship).
public class ChannelUser
{
public string ChannelId {get; set;}
public string UserId {get; set;}
}
The primary key for ChannelUser is (ChannelId, UserId).
One (and only one) of these ChannelUsers can be the Owner of the Channel.
Theoretically, a User can be the Owner of many channels (but obviously a ChannelUser can only be the owner of the channel it is associated to).
I want to enforce that at database level rather than having a "IsOwner" property on ChannelUser and using business logic to ensure that the flag is only set once per channel. I want to also enforce that the ChannelOwner is one of the ChannelUsers and therefore don't want a relationship directly from Channel > User but keep it as Channel > ChannelUser.
Therefore I have updated Channel class as follows:
public class Channel
{
public string Id {get; set;}
public List<ChannelUser> {get; set;}
public ChannelUser Owner {get; set;}
}
And added the following fluent API expressions:
modelBuilder.Entity<ChannelUser>().HasKey(cu => new { cu.ChannelId, cu.UserId });
modelBuilder.Entity<ChannelUser>().HasOne<Channel>(x => x.Channel).WithMany(x => x.ChannelUsers)
.OnDelete(DeleteBehavior.NoAction);
modelBuilder.Entity<ChannelUser>().HasOne<User>(x => x.User).WithMany(x => x.UserChannels)
.OnDelete(DeleteBehavior.NoAction);
When adding migration this results in:
Both relationships between 'ChannelUser.Channel' and 'Channel.ChannelUsers' and between 'ChannelUser' and 'Channel.Owner' could use {'ChannelId'} as the foreign key. To resolve this configure the foreign key properties explicitly on at least one of the relationships.
Which I understand but I think this is what is desired. Because ChannelUser.ChannelId should always be the same Channel.Id for both relationships.
Please can anyone suggest how I can craft this relationship or an alternative approach that still enforces the many to many and the one to one relationships between the tables?
This isn't ideal approach but can be done, have a look at this ER diagram :
Ideal approach would be having proper Entity Mapping like :
User Class :
public class User
{
public string Id {get; set;}
public virtual List<Channel> Channels {get; set;}
// Other properties....
}
Channel Class :
public class Channel
{
public string Id {get; set;}
public List<User> Users {get; set;}
public User Owner {get; set;}
public string? OwnerId { get; set; }
public User Owner { get; set; }
}
ChannelUser Class to join the two:
public class ChannelUser
{
public string Id {get; set;}
public string ChannelId {get; set;}
public string UserId {get; set;}
}
Mapping :
modelBuilder.Entity<User>()
.HasMany<Channel>(user => user.channels)
.WithMany(channel => channel.users)
.Map(cu =>
{
cu.MapLeftKey("UserId");
cu.MapRightKey("ChannelId");
cu.ToTable("ChannelUser");
});
// configure one-to-many relationship for ownership
modelBuilder.Entity<Channel>()
.HasRequired<User>(c => c.User)
.WithMany(u => u.ChannelId )
.HasForeignKey<int>(c => c.UserId);
Responsibility of checking if user exists should be fairly easy in controller code after normalizing this, or we can use DB procedure, trigger to create constrain that checks if given owner ID is indeed in ChannelUser table for given channel.
Following the ForeignKey docs, and multiple examples online I was under the influence that if I give my property (foreign key) this attribute, it would get replaced in a Html.Display call by the first textual property of the parent table.
This doesn't happen and all I get is the same foreign key field.
Does this work in db first applications, and if so, how do I make it work (using ForeignKey)?
Thanks.
EDIT: Or is this Scaffolding exclusive behaviour?
UPDATE: Example code:
// Entity model in Case.cs
public partial class Case
{
public int ID {get; set;}
public string Description {get; set;}
public int Classification_ID {get; set;}
public virtual Classification Classification {get; set;}
}
// Entity model in Classification.cs
// It's a lookup table
public partial class Classification
{
public int ID {get; set;}
public string Label {get; set;}
}
// File with partials
[MetadataType(typeof(CaseMetadata))]
public partial class {}
public class CaseMetadata
{
[ForeignKey("Classification")]
public int Classification_ID {get; set;}
}
I seem to be struggling with a combination of naming conventions and understanding.
I have inherited a database and am building a MVC site that I was unable to get the "database-first" workflow to play nicely. In the end I manually constructed my context classes and have been working away happily.
I am now in a situation where I am unable to add an entity with a relationship to several other existing entities (the many-to-many).
My database looks (simplified for this question) like this:
ListItem Option OptionListItems
====== ====== ===============
Id Id ListItem_Id
Name Name Option_Id
My context contains a property that allows me to get all of my ListItems:
public virtual DbSet<ListItem> ListItems { get; set; }
And if I use some LINQ, I do something like the following, and the items are returned and the many-to-many relationship is satisfied and I get a list of Option within my ListItem:
var item = _context.ListItems
.Where(p => p.Id == id)
.Include(p => p.Options)
.SingleOrDefault();
In fact, I had to construct the cross-reference table in the database manually which I did when I tried to run the above query and the exception I got told me I had no object called dbo.OptionListItems. So I assumed we were all good.
Now I need to create a new ListItem and link it to one or more existing Option and I'm at a loss.
Once I've created my new ListItem in isolation, and attempt to call listItem.Options.Add(...) it fails, but I also get the exact same exception if I try to get a reference to a particular Option and try to do option.ListItems.Add(...).
The error is kind of amusing and is the opposite table name to what I have:
{"Invalid object name 'dbo.ListItemOptions'."}
I suspect that it goes against the grain of EF to build a type and a property on my context to directly access the cross reference table like this:
public virtual DbSet<OptionListItem> OptionListItems { get; set; }
But I'm completely baffled by the pattern to create new relationships.
We have this (many-to-many) working declaratively. Pseudocode:
public class ListItem
{
public ListItem()
{
this.RelatedOptions = new HashSet<OptionListItems>();
}
[Key]
public int Id {get; set;}
public string Name {get; set;}
public virtual ICollection<OptionListItems> RelatedOptions {get; set;}
}
public class Option
{
public Ortion()
{
this.RelatedItems = new HashSet<OptionListItems>();
}
[Key]
public int Id {get; set;}
public string Name {get; set;}
public virtual ICollection<OptionListItems> RelatedItems {get; set;}
}
public class OptionListItems
{
[Key]
public int Id {get; set;}
[Column("ListItemId", Order = 1)]
[ForeignKey("ParentListItem")]
public int ListItemId {get; set;}
[Column("OptionId", Order = 2)]
[ForeignKey("ParentOption")]
public int OptionId {get; set;}
public virtual ListItem ParentListItem {get; set;}
public virtual Option ParentOption {get; set;}
}
This should create full relationship declaratively
Credit goes to Steve Greene for pointing me in the right direction.
The table I had was created by convention and worked when I queried WorkItem with .Include(p => p.Options) however the convention seems to break down if you try to do an update. I'm unsure why, but the construction of the mapping table seems to be <Entity1>+<Entity2>+s when querying, but <Entity2>+<Entity1>+s when updating...
The good news, by using fluentAPI, I've created a specific mapping between the entities and forced the cross reference table and both querying and updating works!
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ListItem>()
.HasMany<Option>(s => s.Options)
.WithMany(c => c.ListItems)
.Map(cs =>
{
cs.MapLeftKey("ListItem_Id");
cs.MapRightKey("Option_Id");
cs.ToTable("OptionListItems");
});
}
I'm not very experienced with EF and I'm trying to figure out what is the proper way or what are the options for creating entity, collection of which can be contained in other entities (different tables).
Let's say I have three existing classes (ModuleA, ModuleB, ModuleD) that I want to contain its own collection of Data entities.
I wanted a single table for Data entities but I don't like the idea of three nullable Guid columns for each FK.
That also gives me error on applying migration ("...may cause cycles or multiple cascade paths") - which could by probably solved by removing cascade delete and deleting Data manually, but I don't like that idea.
What I would like most is the single (shared) Guid property on Data for FKs to all three modules, which is not possible at least not without same ID existing in all three main tables at the same time (since it creates three FKs in DB).
class Data
{
public int Id {get; set;}
public byte[] Values {get; set;}
}
class ModuleA
{
public Guid Id {get; set;}
public ICollection<Data> Data {get; set;}
// some other stuff...
}
class ModuleB
{
public Guid Id {get; set;}
public ICollection<Data> Data {get; set;}
// ...
}
class ModuleC
{
public Guid Id {get; set;}
public ICollection<Data> Data {get; set;}
// some different other stuff...
}
You can create 3 module-to-data many-to-many tables like this:
class Data
{
public int Id {get; set;}
public byte[] Values {get; set;}
}
class ModuleAData
{
public ModuleAId {get; set;}
public DataId {get; set;}
}
class ModuleA
{
public Guid Id {get; set;}
public ICollection<ModuleAData> Data {get; set;}
// some other stuff...
}
EntityFramework 5.0
Suppose I have the following setup:
public class Book
{
public int ID {get; set;}
public string Title {get; set;}
[InverseProperty("Books")]
[Required]
public Author Author {get; set;}
}
public class Author
{
public int ID {get; set;}
public string Name {get; set;}
public virtual ICollection<Book> Books {get; set;}
}
Then in my code I create a new Book and I do this:
author.Books.Add(newBook);
How can I have the Book pick-up its Author automatically instead of having to write this every time:
newBook.Author = author;
I want the child entity to pick up its parent automatically when added to the parent's collection.
Is this possible? Fluent mapping maybe?
Or do I have to maintain both sides of this bi-directional relationship myself?
My mistake.
This is the default behavior and the book gets its Author out-of-the-box.
Case closed.