I'm currently using conditional mappings in EF4 to filter out any records where their IsActive column is false. This works as intended, but I'm running into an issue when navigating relationships.
As an example, I have a one-to-many relationship where a Store can have many Inventory records. A Store can be marked IsActive as can the Inventory records that belong to it. Querying directly for records in those tables works as intended (only active records are returned), but I am also able to retrieve Inventory records where the associated store is not active. This allows the inactive Store to be accessed and gives me no way of detecting whether the store is active.
Prior to switching over to using conditional mapping, we were using queries similar to the following:
Inventories.Where(i => i.IsActive && i.Store.IsActive && i.Product.IsActive && i.Product.Id == productId);
I was hoping we could simplify that query to this:
Inventories.Where(i => i.Product.Id == productId);
This doesn't work for me though, as I no longer have any way of knowing whether the Store or Product are active (as I can't use conditional mapping on IsActive while still mapping that column).
Is there any way of replicating that query while taking advantage of conditional mapping in EF4? Am I forced to leave conditional mapping out of this and hope that all queries make sure to check all relevant IsActive fields?
You could write an expression visitor to add those properties to your query.
Example:
public abstract class ActiveObject
{
public bool IsActive { get; set; }
protected ActiveObject()
{
this.IsActive = true;
}
}
public class Inventory : ActiveObject
{
public Product Product { get; private set; }
public Store Store { get; private set; }
public Inventory()
{
this.Store = new Store();
this.Product = new Product { Id = 10 };
}
}
public class Product : ActiveObject
{
public int Id { get; set; }
}
public class Store : ActiveObject
{
public int Id { get; set; }
}
class Program
{
static void Main()
{
Expression<Func<Inventory, bool>> expression = i => i.Product.Id == 10;
Expression<Func<Inventory, bool>> expression2 = Rewrite(expression);
}
private static Expression<Func<Inventory, bool>> Rewrite(Expression<Func<Inventory, bool>> lambdaExpression)
{
var inventory = lambdaExpression.Parameters[0];
return Expression.Lambda<Func<Inventory, bool>>(
Expression.AndAlso(
Expression.AndAlso(
Expression.Property(
inventory,
"IsActive"
),
Expression.AndAlso(
Expression.Property(
Expression.Property(
inventory,
"Store"
),
"IsActive"
),
Expression.Property(
Expression.Property(
inventory,
"Product"
),
"IsActive"
)
)
),
lambdaExpression.Body
),
inventory
);
}
}
While the other answer may work for some, we decided to map the EF EntitySet to a view instead. The view joins to appropriate tables and checks their appropriate IsActive fields. The resulting portion of our EDMX looks similar to the following:
<EntitySet Name="Inventory" EntityType="Model.Store.Inventory" store:Type="Views" store:Schema="dbo" store:Name="Inventory">
<DefiningQuery>SELECT
[ActiveInventory].[InventoryId] AS [InventoryId],
{Other columns being selected}
FROM [dbo].[ActiveInventory] AS [ActiveInventory]</DefiningQuery>
</EntitySet>
Related
I have an index.chtml set up with about 10 ActionLinks. Those actionLinks trigger different ActionResult functions within the controller since each of them essentially perform unique queries on a data model.
I also have an entities object named db which has all the data. Instead of just displaying all the data, I want to perform complex filtering on the entities object to find where certain properties of records are null or where a property is greater than some input then returns a view of all columns on only those records that were filtered.
Find nulls:
public class printJobsController : Controller {
private PrintJobsEntities db = new PrintJobsEntities
public ActionResult IncompleteJobs {
//get jobs where processDate is null
...
}
}
Find where count is greater than 10:
public class printJobsController : Controller {
private PrintJobsEntities db = new PrintJobsEntities
public ActionResult JobsGreaterThan(int limit) {
//group by printerName and find counts greater than limit
...
}
}
How do I go about doing this?
Seems you are trying to populate the View with filtered data as per your request parameter in controller action.
You could follow the below steps to achieve what you are trying to:
Your imaginary Data Model
public class PrinterJob
{
[Key]
public int PrinterId { get; set; }
public string PrinterName { get; set; }
public int PrintedBy { get; set; }
public int TotalPrint { get; set; }
}
Sample Data In Database:
Controller Action:
public ActionResult <PrinterJob> JobsGreaterThan(int limit) {
var printCountByGroup =
(from objPrint in _context.PrinterJobs group objPrint by new {
objPrint.PrinterName, objPrint.PrintedBy, objPrint.TotalPrint
}
into grp where grp.Sum(p => p.TotalPrint) > limit
select new {
PrinterName = grp.Key.PrinterName, PrintedBy = grp.Key.PrintedBy,
TotalPrint = grp.Key.TotalPrint
});
return View(printCountByGroup);
}
Output After Applying Filter:
Note: Here I am trying to filter printer information which printed more then 30 printing jobs.
Hope it would help you to achieve your goal. If you still have any problem feel free to let me know.
I'm using NHibernate 3.33 and QueryOver with Postgre 9.2.
I've got two entities:
public class User {
public virtual string Name { get; set; }
public virtual IList<Reports> Reports { get; set; }
}
and
public class Report {
public virtual string Type { get; set; }
public virtual DateTime ReportDate { get; set; }
public virtual User Author { get; set; }
}
with association - one-to-many (I didn't append additional fields to entities like Id or Name to snippets above). Some report's types are avaliable - month, day.
My goal is to get summary for user - find out whether user has day-report and month-report for current day.
Note: month-report's ReportDate looks like first day of month. Also I want to get it as one row (if it was an SQL) to transform to dto:
public class UserSummaryDto {
public bool HasDayReport { get; set; }
public bool HasMonthReport { get; set; }
}
To achieve my goal I've tried following:
Report dayReport = null;
Report monthReport = null;
var currentDay; // some value of current day
var firstDay; // some value of first day of month
var report = session.QueryOver<User>
.Left.JoinAlias(u => u.Reports, () => dayReport, r => r.ReportDate == currentDay)
.Left.JoinAlias(u => u.Reports, () => monthReport, r => r.ReportDate == firstDat)
.SelectList(
// some logic to check whether user has reports
.TransformUsing(Transformers.AliasToBean<UserSummaryDto>())
.List<UserSummaryDto>()
And I've got error:
'duplicate association path:Reports'.
Is it possible to avoid this problem or it's a limitation of HNibernate?
To answer your question:
...Is it possible to avoid this problem or it's a limitation of HNibernate?
Have to say NO.
For more information see similar Q & A: Rename NHibernate criteria
We are not querying the DB, not using SQL (which does allow to do a lot). Here we work with "mapped" domain model, and that could bring some limitations - as the one discussed here...
If that could help, the workaround is to map such property twice and use the WHERE clause: 6.2. Mapping a Collection
where="" (optional) specify an arbitrary SQL WHERE condition to be used when retrieving or removing the collection (useful if the collection should contain only a subset of the available data)
Imagine a database table that looks like this:
create table [dbo].[user]
(
id int IDENTITY(1,1),
username varchar(50) NOT NULL,
firstname varchar(20) NOT NULL,
lastname varchar(30) NOT NULL,
currentid int NULL,
processedby varchar(50) NOT NULL,
processeddate varchar(50) NOT NULL
processedaction varchar(50) NOT NULL
)
What I want to do is to setup NHibernate to load it into my user object, but I only want the current version of the object "user" to be brought back. I know how to do a SQL select to do this on my own, and I feel as if there's something in nHibernate with the usage of triggers and event listeners, but can anyone tell me how to implement the nHibernate repository so I can:
{Repository}.GetCurrent(id) <- pass it any of the ids that are assigned to any of the historical or the current record, and get back the current object.
{Repository}.Save(user) <- I want to always insert the changes to a new row, and then update the old versions to link back to the new id.
Edit
So, there's some confusion here, and maybe I explained it wrong... What I'm trying to do is this, in regards to always getting the current record back...
Select uc.*
FROM User uo
JOIN User uc on uo.currentid=uc.id
WHERE uo.id==:id
But, I don't want to expose "CurrentID" to my object model, since it has no bearing on the rest of the system, IMHO. In the above SQL statement, uo is considered the "original" object set, and uc is considered the current object in the system.
Edit #2:
Looking at this as a possible solution.
http://ayende.com/blog/4196/append-only-models-with-nhibernate
I'm honestly being pigheaded, as I'm thinking about this backward. In this way of running a database, the autoincrementing field should be the version field, and the "id" field should be whatever the autoincrementer's value has at the time of the initial insert.
Answer:
I don't want to take #Firo's fury, and I'm not going to remove it from him, as he took me down the right path... what I wound up with was:
Created a base generic class with two types given
a. type of the object's "ID"
b. type of the object itself.
instantiate all classes.
create a generic interface IRepository class with a type of the object to store/retrieve.
create an abstract generic class with a type of the object to store/retrieve.
create a concrete implementation class for each type to store/retrieve.
inside of the create/update, the procedure looks like:
Type Commit(Type item)
{
var clone = item.DeepClone();
_Session.Evict(item);
clone.Id = 0;
clone.ProcessedDate = DateTime.Now;
if (clone.Action.HasValue)
{
if (clone.Action == ProcessedAction.Create)
clone.Action = ProcessedAction.Update;
}
else
{
clone.Action = ProcessedAction.Create;
}
clone.ProcessedBy = UserRepos.Where(u => u.Username == System.Threading.Thread.CurrentPrincipal.Identity.Name).First().Current;
var savedItem = (_Session.Merge(clone) as Type);
_Session.CreateQuery("UPDATE Type SET CurrentID = :newID where ID=:newID OR CurrentID=:oldID")
.SetParameter("newID", savedItem.Id)
.SetParameter("oldID", item.Id)
.ExecuteUpdate();
return savedItem;
}
In the delete method, we simply update the {object}.Action = ProcessedAction.Delete
I wanted to do this another way, but realizing we need to eventually do historical comparisons, we weren't able to ask nHibernate to filter the deleted objects, as the users will want to see that. We'll create a business facade to take care of the deleted records.
Again, much thanks to #Firo for his help with this.
So, with all that, I can finally do this:
var result = {Repository}.Where(obj => obj.Id == {objectID from caller}).FirstOrDefault();
if (result != null)
{
return result.Current;
}
else
{
return null;
}
and always get my current object back for any requesting ID. Hope it helps someone that is in my situation.
in mapping if you use FluentNHibernate
public UserMap : ClassMap<User>
{
public UserMap()
{
Where("id = currentid"); // always bring back the most recent
}
}
// in Userrepository
public void Update(User user)
{
var clone = user.Clone();
session.Evict(user); // to prevent flushing the changes
var newId = session.Save(clone);
session.CreateQuery("UPDATE User u SET u.currentid = :current") // <-- hql
.SetParameter("current", newId)
.ExecuteUpdate();
}
objectgraphs are a lot trickier with this simple code. I would then do one of the following:
use NHibernate.Envers to store auditing information for me
explicitly creating new entities in BL code
i once saw an append-only-model doing something like the following
// UserBase is there to ensure that all others referencing the User doesnt have to update because user properties changed
class UserBase
{
public virtual int Id { get; set; }
public virtual ICollection<PersonDetails> AllDetails { get; private set; }
public virtual PersonDetails CurrentDetails
{
get { return _currentDetauils; }
set { _currentDetauils = value; AllDetails.Add(value); }
}
// same as above
public virtual ICollection<ConfigDetails> AllConfigs { get; set; }
}
class Order
{
public virtual int Id { get; set; }
public virtual UserBase User { get; set; }
public virtual IList<OrderDetail> AllDetails { get; private set; }
public virtual IList<OrderDetail> ActiveDetails { get; private set; }
public virtual void Add(OrderDetail detail)
{
AllDetails.Add(detail);
ActiveDetails.Add(detail);
}
public virtual void Delete(OrderDetail detail)
{
detail.Active = false;
ActiveDetails.Remove(detail);
}
}
class OrderDetail
{
public virtual int Id { get; set; }
public virtual Order Parent { get; set; }
public virtual bool Active { get; set; }
}
class OrderMap : ClassMap<Order>
{
public OrderMap()
{
HasMany(o => o.AllDetails);
HasMany(o => o.ActiveDetails).Where("active=1");
}
}
// somewhere
public void UpdateTaxCharge(OrderDetail detail, TaxCharge charge)
{
var clone = detail.Clone();
clone.TaxCharge = charge;
detail.Order.Delete(detail);
detail.Order.Add(clone);
}
You can tell NHibernate what exactly SQL it should generate when persisting and loading an entity. For example you can tell NHibernate to use a stored procedure instead of a plain SQL statement. If this is an option for you I can farther elaborate my answer.
I want to select all requests that are outstanding for a given manager. A manager can have multiple teams.
I compose the queries applying various restrictions based upon permissions, and alter the queries to provide row counts, existence checks, sub queries, etc.
The composition makes use of QueryOver, though using ICriteria instead would also be acceptable.
Given the following classes;
class Team {
public virtual int Manager { get; set; }
public virtual ISet<int> Members { get; set; }
}
class Request {
public virtual int Owner { get; set; }
public virtual bool IsOutstanding { get; set; }
}
class static SomeRestrictions {
public static void TeamsForManager<TRoot> (this IQueryOver<TRoot, Team> query, int managerId) {
// In reality this is a little more complex
query.Where (x => x.Manager == managerId);
}
}
This is the current query that I'm trying (which doesn't work).
var users = QueryOver.Of<Team> ();
users.TeamsForManager (5)
users.Select (/* not sure */);
var requests = session.QueryOver<Request> ()
.Where (x => x.IsOutstanding)
.WithSubquery.WhereProperty (x => x.Owner).In (users);
The HQL to select the users would be:
"SELECT m FROM Team t JOIN t.Members m WHERE <TeamsForManager restrictions>"
But I don't want to use HQL because I can't then compose it with other restrictions based upon permissions. I also wouldn't be able to compose it with other queries to turn it into row counts/existence checks, etc.
i saw you changed the model but this would have been the way
var users = QueryOver.Of<Team> ();
users.TeamsForManager (5);
users.JoinAlias(t => t.Members, () => membervalue).Select(() => membervalue);
We have some old tables with legacy schemas that we find it hard to work with.
Is it possible to use NHibernate to transform some of this data into a nicer model?
For example we might have an integer status column that we want to break down into several properties.
Legacy status: 0 - Active, 1 - Inactive, 2 - TemporarilyInactive, 3 - etc
We'd want to have something like:
bool IsActive { get; set; }
Status Status { get; set; }
(where Status is an enum)
I know that we can use a protected field that can take the status and then define the getters for the extra properties to return the appropriate value based on the backing field, but I'm pretty sure that this will disable the ability to query based on these properties.
Through this however, we wouldn't be able to do queries such as .Query(p => p.IsActive) and get that translated to SQL such as where status = 0, right?
Is the only way through custom IUserTypes? If so, is there any helper framework that makes working with IUserType easier in order to achieve this?
How do other people handle this?
You can create your own "enumeration" class.
public class Status
{
//The numeric (legacy) code
public int Code{ get;private set; }
//The human readable name
public string Name{ get; private set; }
//If this status is an active status
public bool IsActive { get; private set; }
private Status(int aCode, string aName, bool anIsActive)
{
Code = aCode;
Name = aName;
IsActive = anIsActive;
}
public static Status ACTIVE = new Status(0, "Active");
public static Status INACTIVE = new Status(1, "Inactive");
//Other status here...
private static Status[] STATUSES = {Active,Inactive,...};
//Returns a status based on the passed in code
public static Status GetByCode(int aCode)
{
return STATUSES.FirstOrDefault(aStatus => aStatus.Code == aCode);
}
}
Then you can have NHibernate set a private variable with the legacy value and have a getter/setter that converts between the enumeration and the legacy value.
private int theLegacyStatus; //This is the value that NHibernate sets
public Status
{
get
{
return Status.GetStatusByCode(theLegacyStatus);
}
set
{
theLegacyStatus = value.Code;
}
}
You can then use this enumeration in NHibernate queries: .Query(p => p.Status.Code)