Join table in mapping with inverse FK - c#

Assume I have two tables:
Table MY_ENTITY
ID: PK
OTHER_ID: FK to table OTHER
Table OTHER
ID: PK
COL: The column I want
My entity looks like this:
class MyEntity : Entity
{
public virtual Column { get; set; }
}
My auto-mapping override looks like this:
mapping.IgnoreProperty(x => x.Column);
mapping.Join("OTHER", x => x.KeyColumn("ID").Optional()
.Map(y => y.Column, "COL");
This works fine and executes without problems, but the join is wrong.
It creates an SQL statement that joins the PK of MY_ENTITY to the column specified in KeyColumn in the table OTHER. Something along the lines of:
select ... from MY_ENTITY e left outer join OTHER o on e.ID = o.ID
However, I need the join to be like this:
select ... from MY_ENTITY e left outer join OTHER o on e.OTHER_ID = o.ID
How to achieve this?

You'll have to add an OtherId property to MyEntity (it doesn't have to be public; it's just for mapping) and use PropertyRef in the Join Key mapping (that's the method name in mapping by code; it's property-ref in XML, you'll have to look it up for Fluent)
Alternatively, map Other as an entity and use a Reference in MyEntity. You can cascade all, so it get's persisted/deleted together with MyEntity.
Then, just project the referenced property (which will not be mapped in MyEntity):
class MyEntity
{
public virtual PropertyType Property
{
get
{
EnsureOther();
return Other.Property;
}
set
{
EnsureOther();
other.Property = value;
}
}
void EnsureOther()
{
if (Other == null)
Other = new Other();
}
public virtual Other { get; set; }
}
class Other
{
public virtual PropertyType Property { get; set; }
}

Maybe you should use a References (many-to-one) mapping instead.
References(x => x.Other, "OTHER_ID")
.Fetch.Join()

Related

use Linq to form a relationship where none exists in the database

I've been using the .Net Core Linq expressions .Include and .ThenInclude to great success with my Entity Framework Core project.
However, I need to combine 2 models that are not in any type of relationship in the database.
My SQL query looks like this:
SELECT * FROM selectedEnzymes se
LEFT JOIN enzymeDefinitions ed ON se.selectedEnzymeID = ed.selectedEnzymeID
WHERE se.ecosystemID = 7
selectedEnzymes and enzymeDefinitions are both models.
But there is no relationship between them even though they both contained a selectedEnzymeID. In the database, there is no key.
So I was wondering, is there a way to use Linq to combine two models in such a way if no relationship exists?
Thanks!
You can use the LINQ Join and Select as you do in SQL.
Starting with something like this as models and list of both Classes:
public class SelectedEnzymes
{
public int SelectedEnzymeId { get; set; }
public int EcosystemId { get; set; }
public string OtherPropertySelectedEnzymes { get; set; }
}
public class EnzymeDefinitions
{
public int SelectedEnzymeId { get; set; }
public string OtherPropertyEnzymeDefinitions { get; set; }
}
List<SelectedEnzymes> selectedEnzymesList = new List<SelectedEnzymes>();
List<EnzymeDefinitions> enzymeDefinitionList = new List<EnzymeDefinitions>();
You are able to do something like this:
var query = selectedEnzymesList // table in the "FROM"
.Join(enzymeDefinitionList, // the inner join table
selected => selected.SelectedEnzymeId, // set the First Table Join parameter key
definition => definition.SelectedEnzymeId, // set the Secont Table Join parameter key
(selected, definition) => new { SelectedEnzyme = selected, EnzymeDefinition = definition }) // selection -> here you can create any kind of dynamic object or map it to a different model
.Where(selectAndDef => selectAndDef.SelectedEnzyme.EcosystemId == 7); // where statement
So I was wondering, is there a way to use Linq to combine two models
in such a way if no relationship exists?
In fact, this is similar to the method of obtaining two related tables.
You can directly use the following linq to achieve:
var data = (from se in _context.SelectedEnzymes
join ed in _context.EnzymeDefinitions
on se.SelectedEnzymeId equals ed.SelectedEnzymeId
where se.EcosystemId == 7
select new { se.Name,se.LocationId, ed.Name,ed.CountryId }).ToList();
Here is the result:

How to map NHibernate variable table reference?

Having a table called ChildTable with 2 columns SourceTable and SourceId and some other tables ParentTable1, ParentTable2, etc.
The Id found in SourceId can be used to join to a parent table when the SourceTable has a value associated with that table (1 -> ParentTable1, 2 -> ParentTable2). For example, to get all the ChildTable rows which are associated to rows in ParentTable1 it would be achieved with this query:
select *
from ChildTable ct
join ParentTable1 pt1
on ct.SourceTable = 1 and ct.SourceId = pt1.Id
I would like to map those 2 ChildTable columns as 1 property per parent table: Parent1, Parent2,... so 1 of them would be not null and the rest of the parent properties would be null:
public class ChildClass
{
public Parent1Class Parent1 { get; set; }
public Parent2Class Parent2 { get; set; }
public Parent3Class Parent3 { get; set; }
.
.
.
}
Question is: how to write the mapping for this case (mapping by code if possible)?
Note: This is for mapping existing tables, refactoring the table schema is not a solution yet (but suggestions are welcome).
Update
For the purpose of querying it seems to be enough to map a ChildClass property Parent1 with:
ManyToOne(property => property.Parent1, map => map.Formula("(select pt1.Id from dbo.ParentTable1 pt1 where SourceTable = 1 and pt1.Id = SourceId)"));
and the Children collection of Parent1Class with:
mapper.Where("SourceTable = 1");
For update/insert it is probably achievable using accessors, will post an update later.
Why don't you use Any?
Class:
public class ChildClass
{
public virtual ParentBase Parent { get; set; }
// beware of proxies when casting... this may not work like this
public Parent1Class Parent1 { get { return Parent as Parent1Class; } }
public Parent2Class Parent2 { get { return Parent as Parent2Class; } }
.
.
.
}
Mapping:
Any(x => x.Parent, typeof(int), m =>
{
m.IdType<int>();
m.MetaType<int>();
m.MetaValue(1, typeof(Parent1));
m.MetaValue(2, typeof(Parent2));
m.Columns(
id => id.Name("SourceId"),
classRef => classRef.Name("SourceTable"));
});
There is also many-to-any, which maps collections of any types into a relation table.
When using it in a query, you can check the .class, or use a subquery:
HQL:
select *
from ChildTable ct join Parent
where pt1.class = Parent1
or
select *
from ChildTable ct
Where ct.Parent in (from Parant2 p where p.Property = 'Hugo')

Entity Framework remove N:M relation by ids

I have a Simple table like this:
User(Id, FirstName, ...) <===> Role(Id, Title, ...)
witch have N:M relation
what i want to do is to remove the relation in between them by having there Ids, so my method should be like this :
public void UnlinkUsersFromRoles(int[] roleIds, int[] userIds)
{
var myContext = new DefaultContext();
// ?? <= how to break the relation without loading unnecessary columns
}
If this is many-to-many relationship, there must be a join table in between: for these tables User and Role, let's say it's called UserRole, and is a simple join table (i.e., no other columns on that table, other than the FK ids to the other two tables):
public class UserRole
{
public int UserId { get; set; }
public int RoleId { get; set; }
}
With an explicitly defined join table such as this, UnlinkUsersFromRoles could be defined as follows:
public void UnlinkUsersFromRoles(int[] roleIds, int[] userIds)
{
using (var context = new DefaultContext())
{
foreach (var ur in context.UserRoles
.Where(u => userIds.Contains(u.UserId))
.Where(r => roleIds.Contains(r.RoleId))
.ToArray())
{
context.UserRoles.Remove(ur);
}
context.SaveChanges();
}
}
You're certainly going to wind up loading all the UserRole rows to be removed in when you call ToArray, but this is certainly better than loading in all the related User and Role rows.
Can you just do this:
var rolesToRemove = myContext.Roles.Where(r=> roleIds.Contains(r.Id)).ToArray();
foreach(var user in myContext.Users.Where(u=> userIds.Contains(u.Id)){
forearch(var var role in rolesToRemove) {
user.Roles.Remove(role);
}
}
myContext.SaveChanges();
When you say:
how to break the relation without loading unnecessary columns
You mean, the above code doesn't fullfill you requeriment?
EDIT:
If you have a explicity relationship class like UserInRoles, you should use John Castleman Answer, or just create one, map the ForeignKeys and use his solution.

Is it possible for one entity to take data from two tables?

Is it possible to have one entity who takes data from two tables?
For example.
Table1 has columns: Id, Name, Table2_Id
Table2 has columns: Id, FullName
I have entity with properties: Id, Name, FullName
What I expect is configure (fluently) entity framework to build query:
select t1.Id, t1.Name, t2.FullName
from Table1 t1 join Table2 t2 on t1.Table2_id = t2.Id
Is it possible without has two separated entities and another one who merge both?
After quick google I find how to merge data from to tables into one entity. I need configure entity framework like this
modelBuilder.Entity<MyEntity>()
.Map( m => {
m.Properties(x=> new { x.Id, x.Name });
m.ToTable("Table1");
})
.Map( m => {
m.Property(x=>x.FullName);
m.ToTable("Table2");
});
But how to tell entity framework to join tables on Table2_Id from Table1 and Id from Table2.
I know I can create view and map entity to view, but if possible I'd like to use the scenario I presented.
Yes, if ID is the same in both tables, then you can make the 1:1 mapping map to a single entity.
You can also do Table Per Concrete Type inheritance, in which the inherited properties go in one table, and the derived properties go in another.
Yes, but first you must to create a view in the Database.
Like this:
>
create view my_view
as
select t1.Id, t1.Name, t2.FullName
from Table1 t1 join Table2 t2 on t1.Table2_id = t2.Id
You must to create a model in the project
like this:
>
public class my_view_model
{
public int Id{ get; set; }
public string Name{ get; set; }
public string FullName{ get; set; }
}
Add the model in context (DbContext)
>
public virtual DbSet<my_view_model> my_view_model{ get; set; }
On Context, on OnModelCreating, add this:
>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<my_view_model>(e =>
{
e.HasNoKey();
e.ToView("my_view");
e.Property(v => v.Id).HasColumnName("Id");
e.Property(v => v.Name).HasColumnName("Name");
e.Property(v => v.FullName).HasColumnName("FullName");
});
base.OnModelCreating(modelBuilder);
}

NHibernate component with a one-to-many relation from parent

Say I have a Queue table and a Job table. In the Job table there is a foreign key column QueueId for the Queue table, i.e.
Queue.Id <-- Job.QueueId
Using Fluent NHibernate it is pretty straightforward to map this to a property in the Queue class, i.e.
/* QueueMap */
HasMany(x => x.Jobs)
.KeyColumnNames.Add("QueueId");
But assume I have a very good reason to have a class inbetween, say something like:
public class Queue
{
public Group Group { get; set; }
}
public class Group
{
public IList<Job> Jobs { get; private set; }
}
Then I need to map this using a Component, i.e.
/* QueueMap */
Component(
x => x.Group,
y => y.HasMany(x => x.Jobs).KeyColumnNames.Add("QueueId")
);
When I do this I get the following:
{"could not initialize a collection:
[Queue.Group.Jobs#832fc413-c282-48e8-8cb6-d2a70b0b8de4]
[SQL: SELECT values0_.QueueId as QueueId1_, values0_.Id as Id1_, values0_.Id
as Id16_0_, (....) FROM dbo.Jobs values0_ WHERE values0_.QueueId=?]"}
Any idea as to what I'm doing wrong...
Solved. This was caused by a mapping problem in the JobMap.

Categories