castle activerecord lazy get id - lazy-loading

I have these two classes
[ActiveRecord("Customers", Lazy=true)]
public class Customer : ActiveRecordBase<Customer>
{
[PrimaryKey(PrimaryKeyType.Identity, Access = PropertyAccess.AutomaticProperty)]
public virtual int Id { get; private set; }
[Property]
public virtual string Name { get; set; }
[HasMany(Lazy = true)]
public virtual IList<Order> Orders { get; set; }
}
[ActiveRecord("Orders", Lazy=true)]
public class Order : ActiveRecordBase<Order>
{
[PrimaryKey(PrimaryKeyType.Identity, Access = PropertyAccess.AutomaticProperty)]
public virtual int Id { get; private set; }
[Property]
public virtual string Product { get; set; }
[BelongsTo(Lazy = FetchWhen.OnInvoke)]
public virtual Customer Customer { get; set; }
}
I want to select all orders and foreach of them, get the id of the customer.
So I do this:
using (new SessionScope())
{
var orders = Order.FindAll();
foreach (var o in orders)
{
int id = o.Customer.Id;
}
}
It works, but it creates a SELECT phrase for each call to o.Customer.Id.
Now, the Order already has the Id of the customer, so how can I get it without revisiting the database??

the following code should allow you to get the id (but I couldn't test it at the moment):
int id = (int)((INHibernateProxy)order.Customer).HibernateLazyInitializer.Identifier;
Greetings
Juy Juka

Related

entity framework join 2 tables without modifying domain models

hey new to entity framework and i have these 2 models
SynchronizationProfile
public class SynchronizationProfileDBEntity
{
public string SyncProfileId { get; set; }
public string Code { get; set; }
public SynchronizationType SyncType { get; set; }
public string Schedule { get; set; }
public string UserName { get; set; }
}
SynchronizationLog
public class SynchronizationLogDBEntity
{
public string SyncLogId { get; set; }
public string SyncProfileId { get; set; }
public DateTime Ts { get; set; }
public SyncLogStatus LogStatus { get; set; }
}
there are no foreign keys on these because my team doesnt do it and get the results directly with LINQ
i want to get everything from SynchronizationLog(table) + Code(which is the name for everySyncProfileId) property from SynchronizationProfile
So i tried left join but for some reason dont get me the results.
var profiles = (from item in context.SynchronizationLogs
join item2 in context.SynchronizationProfiles
on item.SyncProfileId equals item2.SyncProfileId into gj
from subitem in gj.DefaultIfEmpty()
select new
{
SyncLogId = item.SyncLogId,
SyncProfileId = item.SyncProfileId,
Ts = item.Ts,
LogStatus = item.LogStatus
Code = item2.Code
}
Is my approach wrong?

Converting SQL query result to Linq result with an new field that is not in the Model

I have an SQL query,
SELECT
c.AssignedTo ,c.CampaignID, c.Name, c.Venue,c.StartedOn,
c.CompletedOn, c.IsOpen, COUNT(C.Name) AS Leads
FROM
Campaign AS c
RIGHT JOIN
Leads AS l ON l.CampaignID = c.CampaignID
GROUP BY
c.AssignedTo, c.CampaignID, c.Name,c.Venue,c.StartedOn, c.CompletedOn, c.IsOpen
ORDER BY
c.AssignedTo
These are my two tables in that query, CAMPAIGNS:
[Table("Campaigns")]
public class Campaigns
{
[Key]
public int CampaignID { get; set; }
[MaxLength(30)]
public string Name { get; set; }
[MaxLength(200)]
public string Venue { get; set; }
[ForeignKey("UsersReference")]
public int AssignedTo { get; set; }
public DateTime StartedOn { get; set; }
public DateTime? CompletedOn { get; set; }
public bool IsOpen { get; set; }
// To Reference Users table using FK
public Users UsersReference { get; set; }
}
and Leads:
[Table("Leads")]
public class Leads
{
[Key]
public int LeadID { get; set; }
[ForeignKey("CampaignsReference")]
public int CampaignID { get; set; }
[MaxLength(30)]
public string ConsumerName { get; set; }
[MaxLength(30)]
public string EmailAddress { get; set; }
[MaxLength(10)]
public string PhoneNo { get; set; }
public string PreferredMoC { get; set; }
public DateTime DateApproached { get; set; }
[ForeignKey("ProductsReference")]
public int ProductID { get; set; }
public string Status { get; set; }
// To access Campaign and Product table through FK
public Campaigns CampaignsReference { get; set; }
public Products ProductsReference { get; set; }
}
My query returns a result likes this in SQL:
The Leads column is just the COUNT() of the columns matching with my CampaignID. I've used context.Campaigns.FromSqlRaw() but since my Campaigns doesn't have another column specifically for the count.
Is there a way to display my new field Count() using EF Core and Linq?
Use LINQ Query for such task.
var query =
from l in ctx.Leads
let c = l.CampaignsReference
group c by new { c.AssignedTo, c.CampaignID, c.Name, c.Venue, c.StartedOn, c.CompletedOn, c.IsOpen } into g
select new
{
g.Key.AssignedTo,
g.Key.CampaignID,
g.Key.Name,
g.Key.Venue,
g.Key.StartedOn,
g.Key.CompletedOn,
g.Key.IsOpen,
Leads = g.Sum(x => x.Name != null ? 1 : 0)
};
var query = query.OrderBy(x => x.AssignedTo);

Get Value from other Table by ID with Entity Framework

i have two already existings tables (no foreignkey):
Customer (Id, Name, .....)
Projects (Id, CustomerId, name)
And in my asp.net core application i have two model:
public class Customer {
public int Id { get; set; };
public String Name { get; set; };
}
public class Project {
public int Id { get; set; };
public Customer Customer{ get; set; };
public String Name{ get; set; };
}
And the datacontext classes for this
public class CustomerContext: DbContext
{
public CustomerContext(DbContextOptions<CustomerContext> options) : base(options)
{
}
public DbSet<CustomerContext> Customer { get; set; }
}
public class ProjectContext: DbContext
{
public ProjectContext(DbContextOptions<ProjectContext> options) : base(options)
{
}
public DbSet<ProjectContext> Project{ get; set; }
}
But i cant find out how to fetch the Customer object in the Projectclass by the customerId
Can someone help me please? Thank you
Edit: Now i change my Model Classes like in the answer below
but with the following i get an SQL Exception while loading the page
SqlException: Invalid object name 'Customer'.
projectList = await (from project in _context.Project
join customer in _customerContext.Customer on project.CustomerId equals customer.Id into tmp
from m in tmp.DefaultIfEmpty()
select new Project
{
Id = sollIst.Id,
CustomerId = sollIst.CustomerId,
Customer = m,
Name = sollIst.Name,
}
).ToListAsync();
Update your model classes as below:
public class Customer {
public int Id { get; set; };
public String Name { get; set; };
}
public class Project {
public int Id { get; set; };
public String Name{ get; set; };
public int CustomerID { get; set; }
[ForeignKey("CustomerID")]
public Customer Customer{ get; set; };
}
Merger both DbContext into one.
public class ProjectContext: DbContext
{
public ProjectContext(DbContextOptions<ProjectContext> options) : base(options)
{
}
public DbSet<Project> Projects { get; set; }
public DbSet<Customer> Customers { get; set; }
}
Then execute
projectList = await (from project in _context.Project
join customer in _context.Customer on project.CustomerId equals customer.Id into tmp
from m in tmp.DefaultIfEmpty()
select new Project
{
Id = sollIst.Id,
CustomerId = sollIst.CustomerId,
Customer = m,
Name = sollIst.Name,
}
).ToListAsync();
I hope following links will help you to know how to join two tables across different database.
Joining tables from two databases using entity framework.
Entity framework join across two databases
You will have to create a property in Project class that represent the "foreign key".
Lets say in Project table in the database the "foreign key" is CustomerID, add this to Project class:
public int CustomerID { get; set; }
Then add the ForeignKey attribute to the Customer property:
[ForeignKey("CustomerID")]
public Customer Customer { get; set; }
First, your model classes should be as follows:
public class Customer {
public int Id { get; set; };
public string Name { get; set; };
}
public class Project {
public int Id { get; set; };
[ForeignKey("Customer")]
public int CustomerId{ get; set; };
public string Name{ get; set; };
public Customer Customer { get; set; };
}
Then your DbContext classes should be as follows:
public class CustomerContext: DbContext
{
public CustomerContext(DbContextOptions<CustomerContext> options) : base(options)
{
}
public DbSet<Customer> Customers { get; set; }
}
public class ProjectContext: DbContext
{
public ProjectContext(DbContextOptions<ProjectContext> options) : base(options)
{
}
public DbSet<Project> Projects { get; set; }
}
Now you can use Entity Framework Core and LINQ query to fetch your desired data.

Ravendb TransformResults showing null values for properties populated with Load()

I have two documents Ticket and MenuItem i have created index with TransformResults but problem is i am getting null value for Loaded document in transform
public class Ticket
{
public int ID { get; set; }
public int ItemId { get; set; }
public string ItemName { get; set; }
public int Price { get; set; }
}
public class MenuItem
{
public int ID { get; set; }
public string ItemName { get; set; }
public string PriceCategory { get; set; }
}
i have created a index like
public class TicketItemGross : AbstractIndexCreationTask<Ticket, TicketItemGross.TicketItemDetails>
{
public class TicketItemDetails
{
public string ID { get; set; }
public string ItemId { get; set; }
public string ItemName { get; set; }
public int Price { get; set; }
public string PriceCategory { get; set; }
}
public TicketItemGross()
{
Map = docs => from doc in docs
select new
{
ID = doc.ID,
ItemId=doc.ItemId,
ItemName=doc.ItemName,
Price=doc.Price
};
TransformResults = (database, docs) => from m in docs
let d = database.Load<MenuItem>(m.ID)
select new
{
ID = m.ID,
ItemId = m.ItemId,
ItemName = m.ItemName,
Price = m.Price,
PriceCategory=d.PriceCategory
};
}
}
and the problem is that when i query data. I get null for PriceCategory but for all other fields i get correct value
here is query
IEnumerable<TicketItemGross.TicketItemDetails> list;
using (var session = store.OpenSession())
{
list = session.Query<TicketItemGross.TicketItemDetails, TicketItemGross>();
}
This is happening because you are using integer IDs. When you call database.Load in your transform, you'll need to manually convert it to a string ID.
database.Load<MenuItem>("MenuItems/" + m.ID)
This is one of several places where Raven gets confused if you use integer or guid IDs. If you use string ids, you won't have this problem.
Also, you might consider using a results transformer instead. They are easier than index transformers, which are now obsolete.

RavenDB TransformResults

I'm trying to use the TransformResults feature, and I can't get it to work. I'm not totally sure I understand this feature, perhaps there is another way to solve this problem. What I want is just the Id from the Order and the email addesses from the Customer and the Entrepreneur. I am happy for all tips that can take me in the right direction. Here is my code.
Document
public class OrderDocument
public string Id {get; set }
public EntrepreneurInfo EntrepreneurInfo { get; set; }
public CustomerInfo CustomerInfo { get; set; }
public OrderStatus CurrentOrderStatus { get; set; }
}
Info classes
public class EntrepreneurInfo
{
public string EntrepreneurDocumentId { get; set; }
public string Number { get; set; }
public string Name { get; set; }
}
public class CustomerInfo
{
public string CustomerDocumentId { get; set; }
public string Number { get; set; }
public string Name { get; set; }
}
The info classes are just subsets of a Customer and Entrepreneur documents respectively.
The Customer and Entrepreneur documents inherits from a base class ( AbstractOrganizationDocument) that has the EmailAddress property.
My Index
public class OrdersApprovedBroadcastingData :
AbstractIndexCreationTask<OrderDocument, OrdersApprovedBroadcastingData.ReduceResult>
{
public OrdersApprovedBroadcastingData()
{
this.Map = docs => from d in docs
where d.CurrentOrderStatus == OrderStatus.Approved
select new
{
Id = d.Id,
CustomerId = d.CustomerInfo.CustomerDocumentId,
EntrepreneurId = d.EntrepreneurInfo.EntrepreneurDocumentId
};
this.TransformResults = (db, orders) => from o in orders
let customer = db.Load<CustomerDocument>(o.CustomerId)
let entrepreneur = db.Load<EntrepreneurDocument>(o.EntrepreneurId)
select
new
{
o.Id,
o.CustomerId,
CustomerEmail = customer.EmailAddress,
o.EntrepreneurId,
EntrepreneurEmail = entrepreneur.EmailAddress
};
}
public class ReduceResult
{
public string Id { get; set; }
public string CustomerId { get; set; }
public string CustomerEmail { get; set; }
public string EntrepreneurId { get; set; }
public string EntrepreneurEmail { get; set; }
}
}
If I look at the result of this Index in Raven Studio I get null values for all fields except the Id. And finally here is my query.
Query
var items =
this.documentSession.Query<OrdersApprovedBroadcastingData.ReduceResult, OrdersApprovedBroadcastingData>()
.Select(x => new OrdersToBroadcastListItem
{
Id = x.Id,
CustomerEmailAddress = x.CustomerEmail,
EntrepreneurEmailAddress = x.EntrepreneurEmail
}).ToList();
Change your index to:
public class OrdersApprovedBroadcastingData : AbstractIndexCreationTask<OrderDocument>
{
public OrdersApprovedBroadcastingData()
{
Map = docs => from d in docs
where d.CurrentOrderStatus == OrderStatus.Approved
select new
{
};
TransformResults = (db, orders) =>
from o in orders
let customer = db.Load<CustomerDocument>(o.CustomerInfo.CustomerDocumentId)
let entrepreneur = db.Load<EntrepreneurDocument>(o.EntrepreneurInfo.EntrepreneurDocumentId)
select new
{
o.Id,
CustomerEmailAddress = customer.EmailAddress,
EntrepreneurEmailAddress = entrepreneur.EmailAddress
};
}
}
Your result class can simply be the final form of the projection, you don't need the intermediate step:
public class Result
{
public string Id { get; set; }
public string CustomerEmailAddress { get; set; }
public string EntrepreneurEmailAddress { get; set; }
}
You don't have to nest this class in the index if you don't want to. It doesn't matter either way. You can query either with:
var items = session.Query<Result, OrdersApprovedBroadcastingData>();
Or with
var items = session.Query<OrderDocument, OrdersApprovedBroadcastingData>().As<Result>();
Though, with the first way, the convention tends to be to nest the result class, so really it would be
var items = session.Query<OrderDocument.Result, OrdersApprovedBroadcastingData>();
Note in the index map, I am not including any properties at all. None are required for what you asked. However, if you want to add a Where or OrderBy clause to your query, any fields you might want to filter or sort on should be put in there.
One last thing - the convention you're using of OrderDocument, CustomerDocument, EntrepreneurDocument, is a bit strange. The usual convention is just Order, Customer, Entrepreneur. Think of your documents as the persisted form of the entities themselves. The convention you are using will work, it's just not the one usually used.