I want to ask you some questions about asp.net mvc5. I'm writing right now a mid-advanced (for me for sure :D) application. What i want to get and what is my problem?
I created a basic MVC page with registration and login from asp.net template. Now i want to extend this page with my ideas.
First: I want to add new entities which will have a foreign keys from other tables (for example from AspNetUser). How can I push data into database when i need as foreign key User ID from table AspNetUser? - I think that in my model class i have to add public ApplicationUser user {get;set;} and ICollection myModels {get;set;} in ApplicationUser class, then in controller my Action must get all parameters (included foreign key), but what next? How can i get User id from table AspNetUser? Can you give me, please, a basic example of this idea?
Second: What is better? Code first or Database first?
Please, help me. This is very important for me, because without this i will not graduate..
You have to look inside EntityValidationErrors to find out what exactly went wrong, if I had to guess I then think it has to do with this line in your ApplicationUser class:
public ICollection<TaskModel> TaskModels { get; set; }
First, you should mark this property with the virtual keyword to enabled lazy loading.
Then you need to configure this One-To-Many relationship in Entity Framework. You can do that by overriding the OnModelCreating method in your OtherDbContext, then configuring the relationship like so:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ApplicationUser>()
.HasMany(e => e.TaskModel)
.WithRequired(e => e.ApplicationUser)
.WillCascadeOnDelete(false);
}
But, you don't really need this unless you plan on using the TaskModels collection, so you can just remove the collection completely and that should fix your problem.
BTW, I personally prefer to implement my foreign key relationships like this:
[ForeignKey(nameof(User))]
public string UserId { get; set; }
public virtual ApplicationUser User { get; set; }
This way my foreign key reference is not a magic string, and it's right next to the navigation property. I find it much cleaner and safer.
I haven't tested any of this code but hopefully I got it right :-)
So.. I created new project and did it all over again. It works even without OnModelCreating and virtual property. I dont know what was wrong, but I guess that was something with Database - I mean migrations etc.
for everyone who has the same problem:
Just add virtual properties to our new model class (before this you have to decide what relation you want to use ;)), then in IdentityModel.cs add another virtual properties to class ApplicationUser and remember to use correct DbContext :)
Thanks guys for help and have a nice day!!!
Related
I have multiple projects that return the same OData entities through a API endpoint. Now i want to call all of the projects and store them in my calling projects database with entity framework.
To add them to the db the ID gets overwritten but i want to save the id that the entity has in the projects database as well. so i can still access them if need be and to check if the data isn't already in my database. Because of this i need to add another MainProjectID and projectID column to the entity.
I tried making a new class that has a reference to the entity i want to save but this used new id's for the entities. I also tried inheriting the class but this gave me key conflict issues, and generics don't work either in entity framework(i'm not saying they should). So i'm kinda at a loss right now.
I basically want to save the id as a non-key. Is there any way i can do this without writing entirely new classes and parsing them manually ?
Any help would be greatly appreciated.
We have multiple alternatives here:
In a distributed system, best way to cope with these kinds of ID clashes is to make IDs globally unique. If you can modify how IDs are generated, that would be my choice to go. You can use a UUID (or Microsoft implementation GUID) that will produce a universal unique identifier. Or if that seems like an overkill you can devise a simple mechanism that combines ID with projectID. However you should ensure that the method you will use will not produce any collisions (no two different id-projectId pair will map to same value).
This will ensure that same entity is used throughout your application and no overlaps occur if you try to put records from different sources into the same table. You only need to implement a mechanism to record which ID originated from which source. You can use a reference entity at aggregator for this purpose. You also need to disable auto increment nature of the ID column so that your global unique values are used in table.
You can use different entities for producing and aggregating applications. I don't know your application, but that seems like an OK approach to me since the aggregating application has a different idea about the entity. The aggregating application cares for which application produced the entity, that might make putting the source application identifier into the entry justifiable. Your entities will only differ in that and when you receive the OData object from API you'll need copy all other properties and put project identifier yourself.
You can use the previous solution, but you can use a derived class in order to not to repeat your object properties. This is a better design alternative. However with this method you'll have some problems with the primary key (as you've stated you had). Consider this example
public class Base {
public int ID { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Name")]
public string Name { get; set; }
}
public class Derived : Base {
[Key]
public int projectId {get; set; }
}
If you don't put [Key] to Derived then you'll have only ID as primary key. When you put [Key] to Derived then you'll have only projectId as primary key. You need to define a composite key and you can do this by removing the [Key] annotation from projectId and using the onModelCreating override of DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Derived>().HasKey(a => new { a.ID, a.projectId })
.Property(c => c.ID).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
modelBuilder.Entity<Derived>().Property(c => c.projectId).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
}
You can mix these alternatives. You can drop the primary key on ID field, and then you can insert a new Entity that will model 1-M relationship between ID's and project ID's.
I am implementing a MVC-Webapplication with ASP.NET Core (RC2) and as ORM Entity Framework Core. Since I already got a database design, I have to create the entity models by the Scaffold-DBContext command.
This works fine. Now, I want to add some annotations to the generated entities in order to add validations. For example MaximumLength:
public class Blog
{
public int BlogId { get; set; }
[MaxLength(500)]
public string Url { get; set; }
}
If there are some database changes, I have to use the scaffold command again. But this results to the loss of some additional annotations. How can I update the entity models without loosing them? According to asp.net page or from this topic, It seems to be possible with EF6. Is there a similar way to achieve this with EF7/Core?
Yes,you can.You have to use Fluent API instead of Data Annotations.
Here is your example using Fluent API
public partial class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.HasMaxLength(500);
}
}
OP's feedback
But the Database context class will be also generated. This means, If
I use the command again, it will replace the old database context.
My suggestion :
You can use partial class here.keep your custom implementation on that file.Those pieces of custom code won't get overwritten when you re-generate the code.
OP's feedback :
I could solve it with partial classes BUT after generating the
entities, you have to go through all entities and delete all
duplicated properties. Still not quite that what I am looking for,
because you still have to modify the entities.
My suggestion :
You don't need to delete any duplicate mappings. B'cos EF gives precedence to the Fluent API.It doesn't matter what ever the mapping has done by the code regeneration automatically. You can overridden those using Fluent API.That is the power of Fluent API.You can also use DataAnnotation and Fluent API at the same time. But code-First gives precedence to Fluent API > data annotations > default conventions.
I saw a lot of examples online but none that I could use from top to bottom about merging my own DbContext with Asp.net IdentityDbContext.
Can someone please walk me through it? I am at step 0, and what I want is to have the tables generated by ASP.net IdentityDbContext inside my own database, so that I can retain user data in my own database. How can I achieve that?
Thanx in advance :)
If I got you correct, you are trying to use your existing database, tables and existing users with asp.net identity framework.
First thing, according to my understanding, you can't merge your db context (MyDbContext) with 'IdentityDbContext', because context of asp.net identity framework tables has to be inherited from IdentityDbContext<YourUserTable>. But your other tables may inherited from DbContext.
Therefore you have to use two separate db contexts if you want to use identity framework build in method support, which is UserManager etc.
You can use your existing database with identity framework, you just need to correctly bind your database tables with identity framework EF code first approach using model binding.
There is a YouTube video tutorial which may help you to get some idea in order to achieve your task. Actually this video illustrates to use Identity 2.0 with existing database.
Part 1: https://www.youtube.com/watch?v=elfqejow5hM
Part 2: https://www.youtube.com/watch?v=JbSqi3Amatw
Hope this helps.
I'm not really sure what you're trying to achieve but check this question to see if it's of any help.
ASP.NET Identity DbContext confusion
Here's part of a walk-through I created. There are a few steps included specific to making it work with code first migrations but you should be able to accomplish what you want using these steps.
Start by creating a new ASP.NET MVC project. Call it Contacts if you want the included code to match. The authentication defaults to Individual User Accounts which is what we want in this case. Deselect Host in the Cloud for now. You can enter your publishing settings later. Once the project is created bring up the Package Manager Console and Install-Package EntityFramework. Now since we are doing Code First add a simple model class.
public class Contact {
public int ContactID { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
}
At this point you would usually add controllers and such but since this post is focusing on the data side of things we'll skip over all of that. Next we want to add our database context. Go ahead and add it right in the Models namespace.
public class ContactContext : IdentityDbContext {
public ContactContext()
: base("ContactContext") {
}
public DbSet<Contact> Contacts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public static ContactContext Create() {
return new ContactContext();
}
}
A couple of things to note here. Since we're consolidating the Identity tables into our application's context we want to inherit from IdentityDbContext instead of just DbContext. Also, I prefer not to use the generated "DefaultConnection" that gets created in Web.config so I'm passing "ContactContext" as the connection string name to the base constructor. We'll modify the connection string in a minute. If you're typing in the OnModelCreating method Visual Studio should add the call to base.OnModelCreating but if not make sure you add it since it's essential for building the identity tables. Although not essential, you can configure the modelBuilder not to pluralize table names. Add a Create method which is needed for the Identity code to use this context. Also, as you add code you'll need to right-click and Resolve to add the appropriate using statements.
As promised, here is the modified connection string to be updated in the web.config in the root of the site. The name property is changed to something that makes sense for the application and the AttachDbFilename and Initial Catalog values are changed to something a little more user friendly than the auto generated name.
<add name="ContactContext" connectionString="Data Source=
(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\ContactContext.mdf;Initial
Catalog=ContactContext;Integrated Security=True"
providerName="System.Data.SqlClient" />
Now go to the Models folder and open the IdentityModels.cs file. Cut the ApplicationUser class from here and paste it into your Contact.cs file. Again you'll have to resolve a few missing namespaces. At this point you can safely delete the IdentityModels.cs file. Since we've eliminated the ApplicationDbContext we'll need to do a find on ApplicationDbContext and replace it with ContactContext in a few places. You should be able to do a clean build.
I am trying to find out what is causing this error, I have listed some of the relevant areas of my code that should hopefully help answer my problem.
The recipe entity's members collection is as shown below:
public virtual IList<Member> Members { get; set; }
and here is the Recipes collection on the member entity:
public virtual IList<Recipe> Recipes { get; set; }
I do the below when creating my DbContext in order to make a many-to-many relationship in a separate table
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// have to specify these mappings using the EF Fluent API otherwise I end up with
// the foreign key fields being placed inside the Recipe and Member tables, which wouldn't
// give a many-to-many relationship
modelBuilder.Entity<Recipe>()
.HasMany(r => r.Members)
.WithMany(m => m.Recipes)
.Map(x => {
x.ToTable("Cookbooks"); // using a mapping table for a many-to-many relationship
x.MapLeftKey("RecipeId");
x.MapRightKey("MemberId");
});
modelBuilder.Entity<Recipe>()
.HasRequired(x => x.Author)
.WithMany()
.WillCascadeOnDelete(false);
}
I also seed my database when the model changes and all I have had to do is add to a recipe's member collection and it seems to be able to sort the rest out for me and place the relevant keys in my cookbook relationship table.
This is some of the code in my recipe controller action that performs the work:
var memberEntity = memberRepository.Find((int)memberId);
var recipeEntity = recipeRepository.Find(recipeId);
recipeEntity.Members.Add(memberEntity);
recipeRepository.InsertOrUpdate(recipeEntity);
recipeRepository.Save();
Here is the insert or update method on my Recipe repository
public void InsertOrUpdate(Recipe recipe)
{
if (recipe.Id == default(int))
{
// New entity
context.Recipes.Add(recipe);
} else
{
// Existing entity
context.Entry(recipe).State = EntityState.Modified;
}
}
I get an error of "InvalidOperationException : The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects." on this line:
context.Entry(recipe).State = EntityState.Modified;
Does anyone know why that would happen? Do I have to add the member to the recipe and vice versa to get this to work? I'm not sure what the problem is because the recipeEntity seems to be the correct one.
Any help would be appreciated, thanks.
EDIT
The context is being created in each repository (RecipeRepository & MemberRepository) as shown, so I presume this is the problem in that a different context is being used for each .Find() request? and that causes problems?
private EatRateShareDbContext context = new EatRateShareDbContext();
I'm not sure this is the solution but it seems like you're using different contexts in your repository.
First make sure your have the same context for each lifetime. lifetime could be different based on your project type. (e.g. for web projects, usually it is the same for each HttpContext). You can use IoC to manage your context lifetime. Good IoC libraries for .Net are autofac and Castle Windsor
Also, I think your call to InsertOrUpdate method is unnecessary (unless you're calling Find method with no tracking.) just remove the line and see if it works:
var recipeEntity = recipeRepository.Find(recipeId);
recipeEntity.Members.Add(memberEntity);
recipeRepository.Save();
One easy way to share your DbContext per HttpRequest is mentioned here.
If you are using AutoFac, you must add SingleInstance() to your register code.
Example:
builder.Register(a => new EntityContainer()).As().SingleInstance()
Where EntityContainer is your DbContext
I have two entities and there are their POCO:
public class DocumentColumn
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual long? DocumentTypeId { get; set; }
}
public class DocumentType {
public virtual long Id { get; set; }
public virtual string Name { get; set; }
}
There is a relation between those two entities. In the db the relation called:FK_T_DOCUMENT_COLUMN_T_DOCUMENT_TYPE.
When I do:
DocumentColumns.Where(x => x.DocumentTypeId == documentTypeId).ToList();
I get the exception:
{"Metadata information for the relationship 'MyModel.FK_T_DOCUMENT_COLUMN_T_DOCUMENT_TYPE' could not be retrieved. If mapping attributes are used, make sure that the EdmRelationshipAttribute for the relationship has been defined in the assembly. When using convention-based mapping, metadata information for relationships between detached entities cannot be determined.\r\nParameter name: relationshipName"}
I tryed to remove the relationship and the DocumentColumn table and reload them but the code still throws the exception.
Whet does this exception means and how can I solve it?
EDIT:
The exception happens also If I do DocumentColumns.ToList();
(Presuming you are talking about Code First ....)
There is no information in either class to let CF know that there is a relationship between them. It doesn't matter that the database has the info. Entity Framework needs to have a clue about the relationship. You provide only a property with an integer. CF cannot infer a relationship. You must have something in one class or another that provides type or another. This is not a database. It's a data model. Very different things.
But that's not all. I'm guessing that this is a one to many relationship. You could either put a List property into the Document class or a Document property in the DocumentColumn class. If you only do the latter, CF and EF will NOT know about the 1:. It will presume a 1:1 (that is if you leave DocumentId integer in there, otherwise it will presume a 1:0..1). However, I think you could get away with this and then just configure the multiplicity (1:) in fluent API.
UPDATE...reading your question again, I think you are using an EDMX and designer not code first. What are you using to create your POCO classes? Are you doing code gen from the EDMX or just writing the classes. I still think the lack of a navigation property in at least ONE of the types might be the cause of the problem. The ERROR message does not suggest that...I'm only coming to this conclusion by looking at the classes and inferring my understanding of how EF works with the metadata. I could be barking up the wrong tree. FWIW, I have asked the team if they are familiar with this exception and can provide some idea of what pattern would create it. It's pretty bizarre. :)
It seems odd to me that you are using EF with a defined relationship and you are not using the related property. Can you not do:
DocumentColumns.Where(x=>x.DocumentType.Id == documentTypeId).ToList();
This is what I would expect to see in this instance.