Mapping one to many relationship to existing database views without keys - c#

I need to create one to many relationship between two classes.Database do not contains primary or foreign keys. So i decided to create 2 views instead of 3 tables, and views also has no primary/foreign key (Changing the database schema is not allowed)
Views
[Table("Barcode")]
public partial class Barcode
{
[Column("_Barcode")]
public string _Barcode { get; set; }
[Column("_SkuRef")]
[MaxLength(16)]
public byte[] _SkuRef { get; set; }
[Key]
[Column("Id")]
public long Id { get; set; }
public string _SkuId { get; set; }
}
[Table("SKU")]
public partial class SKU
{
[Column("_Code", Order = 0)]
[StringLength(11)]
public string _Code { get; set; }
[Column("_Description", Order = 1)]
[StringLength(150)]
public string _Description { get; set; }
[Column("_ProductId", Order = 2)]
[StringLength(50)]
public string _ProductId { get; set; }
[Column("_Ref", Order = 3)]
[MaxLength(16)]
public byte[] _Ref { get; set; }
[Key]
[Column(Order = 4)]
[StringLength(36)]
public string Id { get; set; }
}
In this way, i tryed to use something like
using (SkuContext db = new SkuContext())
{
var Skutable = db.SKUs;
foreach(var s in Skutable)
{
Console.WriteLine(s.Id);
}
}
And its working well. But when im trying to add
public partial class Barcode
{
....
[ForeignKey("_SkuId")]
[Column("_SkuId")]
[StringLength(36)]
public string _SkuId { get; set; }
}
public partial class SKU
{
....
public IEnumerable<Barcode> Barcodes;
}
I'm facing
"The property '_SkuId' cannot be configured as a navigation property" error.
And my question is how to do the right thing? How to create relationship between two classes like these ?

I think you can create a one-to-many relationship between your SKU and Barcode views where one SKU has many Barcodes in the same way you would do it if it were tables. Use Fluent API to configure a one-to-many relationship between SKU and Barcode:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// one-to-many relationship
// _SkuId foreign key in Barcode
modelBuilder.Entity<Barcode>()
.HasRequired(c => c.SKU)
.WithMany(t => t.Barcodes)
.Map(m => m.MapKey("_SkuId"));
}
Update your classes like the following:
[Table("Barcode")]
public partial class Barcode
{
[Column("_Barcode")]
public string _Barcode { get; set; }
[Column("_SkuRef")]
[MaxLength(16)]
public byte[] _SkuRef { get; set; }
[Key]
[Column("Id")]
public long Id { get; set; }
// need to comment this out
//[Column("_SkuId")]
//[StringLength(36)]
//public string _SkuId { get; set; }
public virtual SKU SKU { get; set; }
}
[Table("SKU")]
public partial class SKU
{
[Column("_Code", Order = 0)]
[StringLength(11)]
public string _Code { get; set; }
[Column("_Description", Order = 1)]
[StringLength(150)]
public string _Description { get; set; }
[Column("_ProductId", Order = 2)]
[StringLength(50)]
public string _ProductId { get; set; }
[Column("_Ref", Order = 3)]
[MaxLength(16)]
public byte[] _Ref { get; set; }
[Key]
[Column(Order = 4)]
[StringLength(36)]
public string Id { get; set; }
public virtual ICollection<Barcode> Barcodes { get; set; }
}

You got error because your table does not contains foreign key. The foreign key attribute will not help cause you don't have such relationship in database.
If you are using ORM, such as Entity Framework, the solution will be to write a stored procedure and to make EF to work with it.
Another way is to implement your own business logic using classes from System.Data.SqlClient by specific reading, writing, etc..
So I consider that such an exercise does not needed in your simple situation. But if you are going just in this straight way, without changing tables, be ready for tons of documentation.

Related

How to bridge a table on itself using EF Core Code-First

This is a operation i have done many times in the past using database-first approach. I'm now trying it with code-first using EF Core and i'm failing horribly.
I have the following model:
public class DataMapping
{
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public string Model { get; set; }
public string Property { get; set; }
public bool IgnoreProperty { get; set; }
[NotMapped] //<-- I had to add this as the migration was complaining that it did not know what the relation was
public List<DataMappingRelation> DataMappingRelations { get; set; }
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
public DateTime? DateModified { get; set; }
}
and a Bridge model that basically creates a relations between two DataMapping items in the same table:
public class DataMappingRelation
{
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[ForeignKey("DataMappingId")]
public long? DataMapping1Id { get; set; }
public DataMapping DataMapping1 { get; set; }
[ForeignKey("DataMappingId")]
public long? DataMapping2Id { get; set; }
public DataMapping DataMapping2 { get; set; }
}
However this call does not work:
return _context.DataMappings.Where(x => x.Model == type.FullName)
.Include(x=>x.DataMappingRelations)
.ToList();
It does not like the Include and throws a null ref exception.
All i basically need to do here is for a given "DataMapping" get all the related DataMapping items based on the relations in the "DataMappingRelations" table.
Yes i have looked at this answer but again, it is an example of two seperate tables, not a single table bridging on itself.
I suspect i have done all of this wrong. How can i get this to work? All the examples i have found are bridging two seperate tables. this would be bridging the same table.
Its many-to-many with self but your whole configuration looks messy.
So first, your DataMapping model class should contain two list navigation properties for two foreign keys in the DataMappingRelation as follows:
public class DataMapping
{
......
public List<DataMappingRelation> DataMapping1Relations { get; set; }
public List<DataMappingRelation> DataMapping2Relations { get; set; }
.........
}
Now remove [ForeignKey("DataMappingId")] attribute from both DataMapping1 and DataMapping2 foreign keys as follows:
public class DataMappingRelation
{
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public long? DataMapping1Id { get; set; }
public DataMapping DataMapping1 { get; set; }
public long? DataMapping2Id { get; set; }
public DataMapping DataMapping2 { get; set; }
}
Then the Fluent API configuration should be as follows:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<DataMappingRelation>()
.HasOne(dmr => dmr.DataMapping1)
.WithMany(dm => dm.DataMapping1Relations)
.HasForeignKey(dmr => dmr.DataMapping1Id)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<DataMappingRelation>()
.HasOne(dmr => dmr.DataMapping2)
.WithMany(dm => dm.DataMapping2Relations)
.HasForeignKey(dmr => dmr.DataMapping2Id)
.OnDelete(DeleteBehavior.Restrict);
}

How to map recipes with ingredients using AutoMapper

I have following RecipeModel, IngredientModel and RecipePartModel classes which represent the DTO classes for the frontend user:
public class RecipeModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public string ImageUrl { get; set; }
public string Description { get; set; }
public IEnumerable<RecipePartModel> RecipeParts { get; set; }
}
public class IngredientModel
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class RecipePartModel
{
public Guid Id { get; set; }
public IngredientModel Ingredient { get; set; }
public string Unit { get; set; }
public decimal Quantity { get; set; }
}
Here are my entity classes:
public class Recipe : BaseEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public string ImageUrl { get; set; }
public string Description { get; set; }
public virtual IEnumerable<RecipePart> RecipeParts { get; set; }
}
public class Ingredient : BaseEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public int Amount { get; set; }
public virtual IEnumerable<RecipePart> RecipeParts { get; set; }
}
public class RecipePart : BaseEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
public Ingredient Ingredient { get; set; }
public Recipe Recipe { get; set; }
public string Unit { get; set; }
public decimal Quantity { get; set; }
}
My question is - how can I map the Recipe to RecipeModel using AutoMapper? I tried something like this but I assume it is bad, because it just join all the RecipeParts for the whole database, am I correct?
public class DomainProfile : Profile
{
public DomainProfile()
{
CreateMap<Ingredient, IngredientModel>().ReverseMap();
CreateMap<Recipe, RecipeModel>()
.ForMember(x => x.RecipeParts, opt => opt.MapFrom(src => src.RecipeParts));
}
}
To answer your question about how to use AutoMapper to map a type to another type, there are many ways of doing this. Documentation is here: http://docs.automapper.org/en/stable/Getting-started.html.
I wrote a console app and got it working in the quickest way I know possible using your code. When I debug this, and check inside recipeModel, it references a list of RecipePartModels with a single RecipePartModel. Inside that RecipePartModel, it references an IngredientModel.
static void Main(string[] args)
{
var profile = new DomainProfile();
Mapper.Initialize(cfg => cfg.AddProfile(profile));
var recipe = new Recipe
{
RecipeParts = new List<RecipePart>
{
new RecipePart()
{
Ingredient = new Ingredient()
}
}
};
var recipeModel = Mapper.Map<Recipe, RecipeModel>(recipe);
Console.ReadKey();
}
To answer your concern about getting all recipes from the database, if you're using Entity Framework, it depends on if you have lazy loading turned on. Lazy loading ensures that, when you get a recipe from the database, the recipe parts will not be loaded. They will only be loaded when you access the recipe part directly later on in the program flow. Lazy loading is turned on by default so this is the default behaviour. If you turn it off, you've enabled eager loading which loads all recipe parts and in turn their ingredient.
This might help: http://www.entityframeworktutorial.net/lazyloading-in-entity-framework.aspx.
There is nothing bad about this mapping. In fact you don't even need the ForMember call as this is the default convention. The mapping will simply convert each element in the entity child collection to a corresponding model object.
Of course, whether you load your entities in an efficient manner is another matter. If you load a large amount of Recipe entities, and lazy load the RecipeParts collections for each, you will have a major "SELECT N+1" problem. But this is not the fault of AutoMapper.

Entity framework, issue saving data in many-to-many relationship

I have issue saving data in many-to-may relationship between two tables that breaks by introducing another table in between, containing primary keys of both. I have code first existing database approach along with repository pattern and unit of work in MVC application
and here is my model classes
Navigation_Functions
public class Navigation_Functions
{
public Navigation_Functions()
{
}
[Key]
public int Function_ID { get; set; }
[StringLength(250)]
[Required(ErrorMessage = "Required Title")]
[Display(Name = "Function Title")]
public string FunctionName { get; set; }
[Required(ErrorMessage = "Required Hierarchy Level")]
[Display(Name = "Hierarchy Level")]
public int Hierarchy_Level { get; set; }
public ICollection<Navigation_FunctionController> Navigation_FunctionController { get; set; }
}
}
Navigation_FunctionController Model
public class Navigation_FunctionController
{
public Navigation_FunctionController()
{
}
[Key]
public int ControllerID { get; set; }
[StringLength(250)]
[Required]
public string ControllerName { get; set; }
public ICollection <Navigation_Functions> Navigation_Functions { get; set; }
}
Junction Model
[Table("Navigation_FunctionInController")]
public class Navigation_FunctionInController
{
public Navigation_FunctionInController()
{
}
[Key]
public int FunctionInController_ID { get; set; }
[Key]
[ForeignKey("Navigation_Functions")]
public int Function_ID { get; set; }
[Key]
[ForeignKey("Navigation_FunctionController")]
public int ControllerID { get; set; }
public Navigation_FunctionController Navigation_FunctionController { get; set; }
public Navigation_Functions Navigation_Functions { get; set; }
}
I have generic repository for CRUD operation
public void InsertEntity(TEntity obj)
{
_DbSet.Add(obj);
}
My ViewModel
public class FunctionsNavigation_ViewModel
{
public Navigation_Functions _Navigation_Functions { get; set; }
public Navigation_FunctionController _Navigation_FunctionController { get; set; }
}
public void CreateFunctionNavigation(FunctionsNavigation_ViewModel _obj)
{
using (var _uow = new FunctionsNavigation_UnitOfWork())
{
try
{
var _navigationFunction = _obj._Navigation_Functions;
_navigationFunction.Navigation_FunctionController = new List<Navigation_FunctionController>();
_navigationFunction.Navigation_FunctionController.Add(_obj._Navigation_FunctionController);
_uow.Navigation_Functions_Repository.InsertEntity(_navigationFunction);
_uow.Save();
}
catch
{
}
}
}
if I remove following line from above code then it save new Navigation_Functions
_navigationFunction.Navigation_FunctionController.Add(_obj._Navigation_FunctionController);
following is screen shot from debug code.
I am wondering if my ViewModel are correct? secondly How Entity Framework knows that it need to put primary keys of two tables in Navigation_FunctionInController?
When your model looks like this...
public class Navigation_Functions
{
...
public ICollection<Navigation_FunctionController> Navigation_FunctionController { get; set; }
}
public class Navigation_FunctionController
{
...
public ICollection <Navigation_Functions> Navigation_Functions { get; set; }
}
...so without a Navigation_FunctionInController class, the junction table in the database (Navigation_FunctionInController) is not represented in the class model. If you have this model code first, EF will create a junction table itself. If you work database first, EF won't create a junction class and in the diagram you will see a pure many to many association, like this: *--*. But this only happens if the junction table only contains the two foreign keys, both of which comprise a compound primary key.
In your model you have an explicit junction class (probably because the table has a primary key field besides the two foreign keys). This means that the many to many association turns into a 1:n:1 association. The class model should essentially look like this...
public class NavigationFunction
{
...
public ICollection<NavigationFunctionInController> NavigationFunctionInControllers { get; set; }
}
public class NavigationFunctionController
{
...
public ICollection <NavigationFunctionInController> NavigationFunctionInControllers { get; set; }
}
public class NavigationFunctionInController
{
public int FunctionInControllerID { get; set; }
[ForeignKey("NavigationFunction")]
public int FunctionID { get; set; }
[ForeignKey("NavigationFunctionController")]
public int ControllerID { get; set; }
public NavigationFunctionController NavigationFunctionController { get; set; }
public NavigationFunction NavigationFunction { get; set; }
}
Note that the foreign key fields don't have to be part of the primary key (also not that I prefer the singular name Navigation_Function and plural names for collections, and no underscores in names).
So what happens in your code is that you have a 1:n:1 association, but you try to manage it like a m:n association by adding items directly to _navigationFunction.Navigation_FunctionController(s) . But EF doesn't track that collection, and the items are not saved.
Instead you have to create a junction class instance...
var nfic = new NavigationFunctionInController
{
NavigationFunction = obj.NavigationFunction,
NavigationFunctionController = obj.Navigation_FunctionController
};
...and save it through your repositories and UoW.

Many-to-many table as model with extra column

I have a code-first MVC4 C# application with these two models
public class ClassRoom {
public int ID { get; set; }
public string ClassName { get; set; }
public virtual ICollection<Pupil> Pupils { get; set; }
}
public class Pupil {
public int ID { get; set; }
public string PupilName { get; set; }
public virtual ICollection<ClassRoom> ClassRooms { get; set; }
}
This is a part of the context
modelBuilder.Entity<Location>()
.HasMany(l => l.ClassRooms)
.WithMany(o => o.Pupils)
.Map(m => m.MapLeftKey("PupilID")
.MapRightKey("ClassRoomID")
.ToTable("PupilClassRoomMM"));
Therefore there is a many-to-many table called "PupilClassRoomMM".
This all worked fine, but now I want to add a column to the PupilClassRoomMM-table.
I tried to make this Model, but the Add-Migration doesn't get it.
public class PupilClassRoomMM
{
[Key, Column(Order = 0)]
public int ClassRoomID { get; set; }
[Key, Column(Order = 1)]
public int PupilID { get; set; }
public bool IsPrimary { get; set; }
}
The error is:
"PupilClassRoomMM: Name: The EntitySet 'PupilClassRoomMM' with schema 'dbo' and
table 'PupilClassRoomMM' was already defined.
Each EntitySet must refer to a unique schema and table."
How can I add a field to an already existing many-to-many table (I don't want to lose its data).

How to set up a complex many to many relationship in entity framework 4 code first

I have a relatively complex relationship I need to set up between a User object and a lot of lookup tables. The user object is your run of the mill user model:
public class Youth : IAuditInfo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public Guid YouthGuid { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
public string ImageName { get; set; }
[ForeignKey("FkYouthId")]
public ICollection<User> Parents { get; set; }
public CubPack Pack { get; set; }
public virtual ICollection<RequirementsLog> RequirementsLogs { get; set; }
public Youth()
{
Parents = new List<User>();
}
}
The lookup tables is where it gets complex and I can't figure out the path of least complexity in binding them together. For the lookups it is a series of tables starting with one 'master' table, that rolls down hierarchically to requirements and sub requirements, like this:
Master:
public class BearTrail
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<BearTrailRequiredBadge> BearTrailRequiredBadges { get; set; }
public virtual ICollection<BearTrailElectiveBadge> BearTrailElectivedBadges { get; set; }
}
Required Badges:
public class BearTrailRequiredBadge
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public int Number { get; set; }
public string Description { get; set; }
public virtual ICollection<BearTrailRequiredBadgeSubRequirement> BearTrailRequiredBadgeSubRequirements { get; set; }
}
Required Badge sub requirement:
public class BearTrailRequiredBadgeSubRequirement
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Number { get; set; }
public string Text { get; set; }
public bool Required { get; set; }
}
This is one set of the lookups, there are about four nested classes like this, and some one off tables as well. Total lookup tables is about 16, give or take.
I was initially thinking if using my RequirementLog model to bind it:
public class RequirementsLog
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual ICollection<Youth> Youth { get; set; }
public BearTrail BearTrailRequirements { get; set; }
public TigerTrail TigerTrailRequirements { get; set; }
public WolfTrail WolfTrailRequirements { get; set; }
public WebelosTrail WebelosTrailRequirements { get; set; }
public WebelosArrowOfLight WebelosArrowOfLightRequirements { get; set; }
}
So there is a many to many between RequirementsLog and Youth. The table created out of RequirementsLog has one PK column (ID), and FK columns for each property. The many to many table created out of this (RequirementsLogYouths) has two PKs (RequirementsLogId, and YouthId).
Am I going about this the right way? The end goal is to have the 16 or so tables server as just lists of various requirements, and have another table(s) to track a particular youths progress through the requirements. I have a hard time visualizes some of this DBA stuff, so any input would be greatly appreciated.
In most cases, a requirements "log" be in a one (people) to many (the log).
Unless... One logged item is for many kids...
If so, the you need a third table, that maps many people to multiple logged events. That is, if this is truly a many to many. In general, that situation almost always begs for a third, intermediate mapping table. Read up a bit on many to many designs, and you'll quickly see it, and how simple it is.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Entity1>()
.HasMany(b => b.Entities2)
.WithMany(p => p.Entities1)
.Map(m =>
{
m.ToTable("Entitie1Entity2");
m.MapLeftKey("Entity1Id");
m.MapRightKey("Entity2Id");
});
}

Categories

Resources