Entity Framework 5 - Many to Many relationship between multiple table using same intermediate table - entity-framework-5

I am trying to implement the below scenario using Entity Framework 5, with code first approach.
Scenario: I have three tables say
Table1: { Id1, Title }
Table2: { Id2, Title }
Table3: { Id3, Title }
and single intermediate table storing many-to-many relationship between "Table1 &Table2" and "Table2 & Table3". Say,
TableIntermediate: { FK1, FK2 }
To specify the relation, On OnModelCreating(), I have specified two model builder:
modelBuilder.Entity<Table1>()
.HasMany(c => c.Table2s)
.WithMany(pc => pc.Table1s)
.Map(m =>
{
m.ToTable("TableIntermediate");
m.MapLeftKey("FK1");
m.MapRightKey("FK2");
});
modelBuilder.Entity<Table1>()
.HasMany(c => c.Table3s)
.WithMany(pc => pc.Table1s)
.Map(m =>
{
m.ToTable("TableIntermediate");
m.MapLeftKey("FK1");
m.MapRightKey("FK2");
});
Below are the entities defined:
public class Table1
{
public int Id1 {get; set;}
public string Title {get; set;}
public ICollection<Table2> Table2s { get; set; }
public ICollection<Table3> Table3s { get; set; }
}
public class Table2
{
public int Id2 {get; set;}
public string Title {get; set;}
public ICollection<Table1> Table1s { get; set; }
}
public class Table3
{
public int Id3 {get; set;}
public string Title {get; set;}
public ICollection<Table1> Table1s { get; set; }
}
The code compiles properly. But at runtime throwing the error:
{"Schema specified is not valid. Errors: \r\n(275,6) : error 0019: The EntitySet 'Table1Table2' with schema 'XXXX' and table 'TableIntermediate' was already defined. Each EntitySet must refer to a unique schema and table."}
As we are using the exiting database, we want to store both the relations into a single intermediate table. The only solution I found is to introduce a new mapping table. Please let me know if the scenario is possible without introducing the new mapping table.

Related

Saving a collection of records in Orchard

Currently I have a part that has 3 fields (Name, Value1, Value2). I have everything working where I can do a Create/Edit/Delete on the part.
What I want to do now is have a grid with 3 columns (Name, Value1, Value2) and can have multiple rows (up to the user how many there will be). The save won't happen until the user done (save all rows in a single post back).
I haven't figured what is needed so a collection of items will get saved on post back.
Any suggestions on how to do this?
Thanks!
What you could have is to have, in the part, a collection of the records corresponding to (Name, Value1, Value2) by having your dbms create and manage a 1-to-n relationship.
For example, you would have
public class ThisIsYourPart : ContentPart<ThisIsYourPartRecord> {
// You can access the list of your records as
// yourPart.Record.YourRecords
}
public class ThisIsYourPartRecord : ContentPartRecord {
public ThisIsYourPartRecord () {
YourRecords= new List<YourRecordWithValues>();
}
public virtual IList<YourRecordWithValues> YourRecords{ get; set; }
}
public class YourRecordWithValues {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Value1 { get; set; } // use your actual type
public virtual ThisIsYourPartRecord ThisIsYourPartRecord { get; set; }
}
public class YourMigration : DataMigrationImpl {
public int Create() {
SchemaBuilder.CreateTable("YourRecordWithValues ", table => table
.Column<int>("Id", col => col.Identity().PrimaryKey())
.Column<string>("Name", col => col.NotNull().Unlimited())
.Column<string>("Value1", col => col.NotNull().Unlimited())
.Column<int>("ThisIsYourPartRecord_Id"));
SchemaBuilder.CreateTable("ThisIsYourPartRecord", table => table
.ContentPartRecord());
}
}
Code like that should do it.
We used this kind of relations a lot in https://github.com/bleroy/Nwazet.Commerce
*edit:
of course, have all the code in the proper files and folders.

ServiceStack - [Reference] or [Ignore]?

We have a DTO - Employee - with many (> 20) related DTOs and DTO collections. For "size of returned JSON" reasons, we have marked those relationships as [Ignore]. It is then up to the client to populate any related DTOs that they would like using other REST calls.
We have tried a couple of things to satisfy clients' desire to have some related Employee info but not all:
We created a new DTO - EmployeeLite - which has the most-requested fields defined with "RelatedTableNameRelatedFieldName" approach and used the QueryBase overload and that has worked well.
We've also tried adding a property to a request DTO - "References" - which is a comma-separated list of related DTOs that the client would like populated. We then iterate the response and populate each Employee with the related DTO or List. The concern there is performance when iterating a large List.
We're wondering if there a suggested approach to what we're trying to do?
Thanks for any suggestions you may have.
UPDATE:
Here is a portion of our request DTO:
[Route("/employees", "GET")]
public class FindEmployeesRequest : QueryDb<Employee> {
public int? ID { get; set; }
public int[] IDs { get; set; }
public string UserID { get; set; }
public string LastNameStartsWith { get; set; }
public DateTime[] DateOfBirthBetween { get; set; }
public DateTime[] HireDateBetween { get; set; }
public bool? IsActive { get; set; }
}
There is no code for the service (automagical with QueryDb), so I added some to try the "merge" approach:
public object Get(FindEmployeesRequest request) {
var query = AutoQuery.CreateQuery(request, Request.GetRequestParams());
QueryResponse<Employee> response = AutoQuery.Execute(request, query);
if (response.Total > 0) {
List<Clerkship> clerkships = Db.Select<Clerkship>();
response.Results.Merge(clerkships);
}
return response;
}
This fails with Could not find Child Reference for 'Clerkship' on Parent 'Employee'
because in Employee we have:
[Ignore]
public List<Clerkship> Clerkships { get; set; }
which we did because we don't want "Clerkships" with every request. If I change [Ignore] to [Reference] I don't need the code above in the service - the List comes automatically. So it seems that .Merge only works with [Reference] which we don't want to do.
I'm not sure how I would use the "Custom Load References" approach in an AutoQuery service. And, AFAIKT, the "Custom Fields" approach can't be use for related DTOs, only for fields in the base table.
UPDATE 2:
The LoadSelect with include[] is working well for us. We are now trying to cover the case where ?fields= is used in the query string but the client does not request the ID field of the related DTO:
public partial class Employee {
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
.
.
.
[References(typeof(Department))]
public int DepartmentID { get; set; }
.
.
.
public class Department {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
.
.
.
}
So, for the request
/employees?fields=id,departmentid
we will get the Department in the response. But for the request
/employees?fields=id
we won't get the Department in the response.
We're trying to "quietly fix" this for the requester by modifying the query.SelectExpression and adding , "Employee"."DepartmentID" to the SELECT before doing the Db.LoadSelect. Debugging shows that query.SelectExpression is being modified, but according to SQL Profiler, "Employee"."DepartmentID" is not being selected.
Is there something else we should be doing to get "Employee"."DepartmentID" added to the SELECT?
Thanks.
UPDATE 3:
The Employee table has three 1:1 relationships - EmployeeType, Department and Title:
public partial class Employee {
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
[References(typeof(EmployeeType))]
public int EmployeeTypeID { get; set; }
[References(typeof(Department))]
public int DepartmentID { get; set; }
[References(typeof(Title))]
public int TitleID { get; set; }
.
.
.
}
public class EmployeeType {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
}
public class Department {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
[Reference]
public List<Title> Titles { get; set; }
}
public class Title {
[PrimaryKey]
public int ID { get; set; }
[References(typeof(Department))]
public int DepartmentID { get; set; }
public string Name { get; set; }
}
The latest update to 4.0.55 allows this:
/employees?fields=employeetype,department,title
I get back all the Employee table fields plus the three related DTOs - with one strange thing - the Employee's ID field is populated with the Employee's TitleID values (I think we saw this before?).
This request fixes that anomaly:
/employees?fields=id,employeetypeid,employeetype,departmentid,department,titleid,title
but I lose all of the other Employee fields.
This sounds like a "have your cake and eat it too" request, but is there a way that I can get all of the Employee fields and selective related DTOs? Something like:
/employees?fields=*,employeetype,department,title
AutoQuery Customizable Fields
Not sure if this is Relevant but AutoQuery has built-in support for Customizing which fields to return with the ?fields=Field1,Field2 option.
Merge disconnected POCO Results
As you've not provided any source code it's not clear what you're trying to achieve or where the inefficiency with the existing solution lies, but you don't want to be doing any N+1 SELECT queries. If you are, have a look at how you can merge disconnected POCO results together which will let you merge results from separate queries based on the relationships defined using OrmLite references, e.g the example below uses 2 distinct queries to join Customers with their orders:
//Select Customers who've had orders with Quantities of 10 or more
List<Customer> customers = db.Select<Customer>(q =>
q.Join<Order>()
.Where<Order>(o => o.Qty >= 10)
.SelectDistinct());
//Select Orders with Quantities of 10 or more
List<Order> orders = db.Select<Order>(o => o.Qty >= 10);
customers.Merge(orders); // Merge disconnected Orders with their related Customers
Custom Load References
You can selectively control which references OrmLite should load by specifying them when you call OrmLite's Load* API's, e.g:
var customerWithAddress = db.LoadSingleById<Customer>(customer.Id,
include: new[] { "PrimaryAddress" });
Using Custom Load References in AutoQuery
You can customize an AutoQuery Request to not return any references by using Db.Select instead of Db.LoadSelect in your custom AutoQuery implementation, e.g:
public object Get(FindEmployeesRequest request)
{
var q = AutoQuery.CreateQuery(request, Request);
var response = new QueryResponse<Employee>
{
Offset = q.Offset.GetValueOrDefault(0),
Results = Db.Select(q),
Total = (int)Db.Count(q),
};
return response;
}
Likewise if you only want to selectively load 1 or more references you can change LoadSelect to pass in an include: array with only the reference fields you want included, e.g:
public object Get(FindEmployeesRequest request)
{
var q = AutoQuery.CreateQuery(request, Request);
var response = new QueryResponse<Employee>
{
Offset = q.Offset.GetValueOrDefault(0),
Results = Db.LoadSelect(q, include:new []{ "Clerkships" }),
Total = (int)Db.Count(q),
};
return response;
}

ServiceStack OrmLite - database first & multiple primary keys

I have to work off an existing Db & would like to use ServiceStack's OrmLite.
Thus I have created Poco classes, using OrmLite T4 templates.
ISSUE: I would like to save to a table which has multiple primary keys.
public partial class DbUserGroup
{
[Required]
public int Userid { get; set;} // this is a primary key
[Required]
public int Groupid { get; set;} // this is a primary key
public int Ranking { get; set;}
public bool Isprimary { get; set;}
}
Currently using Db.Save(userGroup) does not work. Is there any way of addressing this using ServiceStack's OrmLite.
Multiple primary keys don't exist. A multi-column primary key yes.
Please take a look on this link https://github.com/ServiceStack/ServiceStack.OrmLite#limitations
As it said
A potential workaround to support tables with multiple primary keys is to create an auto generated Id property that returns a unique value based on all the primary key fields
I resolved it by adding [PrimaryKey] to both properties.
public partial class DbUserGroup
{
[Required]
[PrimaryKey]
public int Userid { get; set;} // this is a primary key
[Required]
[PrimaryKey]
public int Groupid { get; set;} // this is a primary key
public int Ranking { get; set;}
public bool Isprimary { get; set;}
}

ServiceStack ORMLite how to not serialize list

I don't know how to store collection (Comments) in separate table.
By default comments are serialized and stored in SomeClass table as column Comments.
[{Id:0,CreateDate:2013-09-12T14:28:37.0456202+02:00,,SomeClassID:1,CommentText:"coment text",}]
Is there any way to save it in separate tables?
public class SomeClass {
[AutoIncrement]
public int Id { get; set; }
public string Title { get; set; }
List<Comment> comments = new List<Comment>();
public List<Comment> Comments {
get { return comments; }
set { comments = value; }
}
}
public class Comment {
[AutoIncrement]
public int Id { get; set; }
[References(typeof(SomeClass))]
public int SomeClassID { get; set; }
[StringLength(4000)]
public string CommentText { get; set; }
}
I don't think ORMLite supports serializing to multiple tables. 1 table = 1 class so the comments will be stored as a Blob field in the SomeClass table.
If you need to store them in separate tables you will have to save the comments separately and have a foreign key reference back to the id of the SomeClass table.

One to Zero or One relationship with EF5 fluent mapping

I have three entities:
public class MainEntity()
{
public long Id { get; set; }
public long EntityAId { get; set; }
public EntityA OptionalEntityA { get; set; }
public long EntityBId { get; set; }
public EntityB OptionalEntityB { get; set; }
public string SProp { get; set; }
}
public class EntityA()
{
public long Id { get; set; }
public long MainEntityId { get; set; }
public MainEntity RequiredEntity { get; set; }
}
public class EntityB()
{
public long Id { get; set; }
public long MainEntityId { get; set; }
public MainEntity RequiredEntity { get; set; }
}
All three entities have there own Id generated by database:
Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
How can I define the Relationship between MainEntity and EntityA and EntityB in order to have:
MainEntity may have zero or one EntityA
MainEntity may have zero or one EntityB
EntityA must have one MainEntity
EntityB must have one MainEntity
In my MainEntityConfigurationMap I have defined relation like this:
HasOptional(m => m.OptionalEntityA).WithRequired(ea => ea.RequiredEntity);
HasOptional(m => m.OptionalEntityB).WithRequired(eb => eb.RequiredEntity);
Looking at the generated migration I have this for EntityA:
CreateTable(
"dbo.EntityA",
c => new
{
Id = c.Long(nullable: false, identity: true),
MainEntityId = c.Long(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.MainEntity", t => t.Id)
.Index(t => t.Id);
Entity Framework is defining a shared primary key here, is there a way to avoid that and make MainEntityId poiting to MainEntity.Id?
Entity framework supports one-to-one relation only on top of shared primary key (PK in dependent table is also FK to principal table). The reason is that FK in dependent table must be unique to enforce one-to-one relation but EF currently doesn't support unique constraint (except PK).
And to add on what #Ladislav Mrmka said...
Here is a full study on the various one-to-one modalities - and particularly how to make it using FK-s instead of tying up PK-s (but don't raise your hopes up too high).
Create a Unique Constraint To Enforce the Relationship as a One to One
It boils down to executing a SQL manually (during Seed-ing or similar Db initialization) - which adds a UNIQUE CONSTRAINT...
However, even in that case it won't work for you - or not w/o changes.
If I'm not mistaking - you'd have to give up one navigation property - and most likely the OptionEntityA/B - as unique column cannot support NULL.

Resources