I am trying to implement privileges using NHibernate, and what I want to do, is each time there is a Select query, check what the return type is, and if it is a security enabled type (such as invoices) i want to add restrictions to the ICriteria object, to restrict retrieving only certain records (According if the user has read all, or read own privileges).
I managed to implement these kind of privileges for Insert and Update using
NHibernater.Event.IPreUpdateEventListener
NHibernater.Event.IPreInsertEventListener
but unfortunately the IPreLoadEventListener is called after the database is queried, and therefore it is waste as the filtering will be done locally on the computer rather then by the database.
Does anyone know if NHibernate provides some sort of event that is called before a query is executed?
If you're able to use it, check out Rhino.Security It does exactly what you're trying to do. Even if you're unable to use it, you can then see his implementation of this problem.
Can't you achieve this by using Filters ?
More information can be found here
I've used this in combination with Interceptors in a project of mine:
I have some entities where each user can create instances from, but only the user who has created them, should be able to see / modify those instances. Other users cannot see instances created by user X.
In order to that, I've created an interface IUserContextAware. Entities that are 'user context aware' implement this interface.
When building my session-factory, I created the necessary filters:
var currentUserFilterParametersType = new Dictionary<string, NHibernate.Type.IType> (1);
currentUserFilterParametersType.Add (CurrentUserContextFilterParameter, NHibernateUtil.Guid);
cfg.AddFilterDefinition (new FilterDefinition (CurrentUserContextFilter,
"(:{0} = UserId or UserId is null)".FormatString (CurrentUserContextFilterParameter),
currentUserFilterParametersType,
false));
When this was done, I needed to define the additional filter criteria:
foreach( var mapping in cfg.ClassMappings )
{
if( typeof (IUserContextAware).IsAssignableFrom (mapping.MappedClass) )
{
// The filter should define the names of the columns that are used in the DB, rather then propertynames.
// Therefore, we need to have a look at the mapping information.
Property userProperty = mapping.GetProperty ("UserId");
foreach( Column c in userProperty.ColumnIterator )
{
string filterExpression = ":{0} = {1}";
// When the BelongsToUser field is not mandatory, NULL should be taken into consideration as well.
// (For instance: a PrestationGroup instance that is not User-bound (that can be used by any user), will have
// a NULL value in its BelongsToUser field).
if( c.IsNullable )
{
filterExpression = filterExpression + " or {1} is null";
}
mapping.AddFilter (CurrentUserContextFilter, "(" + filterExpression.FormatString (CurrentUserContextFilterParameter, c.Name) + ")");
break;
}
}
Now, whenever I instantiate an ISession, I specify that a certain interceptor should be used:
This interceptor makes sure that the parameter in the filter is populated:
internal class ContextAwareInterceptor : EmptyInterceptor
{
public override void SetSession( ISession session )
{
if( AppInstance.Current == null )
{
return;
}
// When a User is logged on, the CurrentUserContextFilter should be enabled.
if( AppInstance.Current.CurrentUser != null )
{
session.EnableFilter (AppInstance.CurrentUserContextFilter)
.SetParameter (AppInstance.CurrentUserContextFilterParameter,
AppInstance.Current.CurrentUser.Id);
}
}
}
Related
I'm using C# Entity Framework to select records from a database table. Based on selection criteria, I will use different select statements.
if ( condition1 )
{
var records = _context.Member.Select( .... )
}
else if (condition2)
{
var records = _context.Member.Select( .....)
}......
And then, I need to make some decisions depending on whether there are records and process those records.
if (records != null)
{
}
else if (....)
The compiler complains that "records" does not exist in the current context. I think the reason why this happens is that records is declared in a if block. I don't want to do the 2nd step inside the if block because the processing is the same and it quite lengthy.
I've tried declaring record outside the block first, but I don't know what Type to use. So how do I declare a variable to hold the records return from Entity Framework?
Thanks.
Edit: After reading the comments and Answer. I think I know where my confusion lies. If my select is a new anonymous object, what should my type be ?
var records = _context.Member
.Select(x => new {Name = x.name, Address = x.address} );
When I hover over the Select, it says:
Returns: An IQueryable<out T> that contains elements from the input sequence that satisfy the condition specified by predicate.
Types: ‘a is new { .... }
As it's an anonymous object, what should I state as the type for it ?
Thanks again for the great help.
What data type is records? Find that out and lets call that T.
If you are using Visual Studio, just hover over the Select method. It will popup some information about method, there is also a return type before the name of the method.
Then write this code:
T records = null; // or some kind of empty collection
if ( condition1 )
{
records = _context.Member.Select( .... )
}
else if (condition2)
{
records = _context.Member.Select( .....)
}
The reason you ran in to this problem is that 'records' is defined only in the scope of '{}' curly braces. By bringing it up like i showed you, you move it to the same context where you have the decisions
if (records != null)
{
}
else if (....)
I currently have a program which interacts with and queries data from a database. However, I want to figure out how I could possibly take a database and a config file and map the table contents to my existing contents.
For example:
I have the class Person. A person has a ID(long), accountName(string), Title(string), firstName(string) and lastName(string).
The new connected database has the same properties for Person but stores them in a different way. They might call the table Individual, with columns UniqueID(long), accName(string), personalTitle(string), fName(string), lName(string).
I know from the config file the names of each column that are the equivalents to the properties, but I'll only be able to read that at run time. Is there a way I could read the titles of what I am looking for and load them into these classes so that I don't have to hard code it every time?
Thanks in advance!
The best thought that comes to my mind is skipping ORM of any kind and just using query strings:
Assuming we are using just plain ADO this way
your query string would look something like:
string queryString =
$"SELECT {ConfigurationManager.AppSettings[productColumn]}
, {ConfigurationManager.AppSettings[PriceColumn]}
, {ConfigurationManager.AppSettings[productNameColumn]}
from dbo.{ConfigurationManager.AppSettings[ProductTableName]} "
+ "WHERE {ConfigurationManager.AppSettings[UnitPriceColumn]} > #pricePoint "
+ "ORDER BY {ConfigurationManager.AppSettings[UnitPriceColumn]} DESC;";
It feels sort of weird, but so are the requirements.
Is it really always random new databases or is it a finite amount ?
You could have a class which includes all of those possible case but will check "on the fly" which one should be used or not.
I haven't tryed that with any ORM, but I remember the "bool ShouldSerializeXXXXXX" prefix when dealing with XML serialization/deserialization, maybe there is an equivalent for your case.
This is pretty easy to implement. When your procedure returns a datatable, you read the mapping from the configuration file and set the value for the class property based on what field name is mapped to the property using reflection.
Here is some code that you can put to work.
/// Your result objects list
List<DBObject> result = new List<DBObject>();
System.Reflection.ConstructorInfo ci = objectType.GetConstructor(new Type[] { });
System.Reflection.PropertyInfo[] props = objectType.GetProperties();
if ((table != null) && (table.Rows.Count > 0) && (ci != null) && (props != null) && (props.Length > 0))
{
for (int i = 0; i < table.Rows.Count; i++)
{
DBObject dob = (DBObject)ci.Invoke(new object[] { });
if (dob != null)
{
foreach (System.Reflection.PropertyInfo pi in props)
{
/// This is your method to get DB column name from config file
string dbColumnName = GetDBColumnFromMappingConfigFile(pi.Name);
object value = null;
if (table.Columns.Contains(dbColumnName))
{
if (table.Rows[i][dbColumnName] != DBNull.Value)
{
value = table.Rows[i][dbColumnName];
}
}
if (value != null)
{
if ((pi.PropertyType.IsGenericType) && (pi.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))))
{
Type t = pi.PropertyType.GetGenericArguments()[0];
if (t != null)
{
object val = Convert.ChangeType(value, t);
pi.SetValue(dob, val, null);
}
}
else
pi.SetValue(dob, Convert.ChangeType(value, pi.PropertyType), null);
}
}
}
}
}
I usually use PropertyAttributes instead of Property names, because attributes allow you to define default values in case if DB has it NULL, define setters, etc.
A couple of ways this could be done, you could do this either application, database, or a combination
Application Side
Hard-code your program classes and build your application on that.
Build the queries to populate the classes based on known queries.
Build a configuration section in the app for selecting which query for which class
Use that configuration to populate your model
Database Side is a little more complicated, but ends up being cleaner in the application. You still create the classes as normal.
On the DB server, there are a multitude of options.
You still are creating your classes and queries in the application
You would do all of your CRUD ops in Stored Procedures, which have the ability to determine if a table exists or not; and then you would simply go through a stack of IF Exists (select tablename...) to determine which query to run
You have a "setup" stored procedure which converts the tables present into a structure that aligns with your apps classes
and many other options
I am looking for help on an issue with NHibernate which has been bugging me for a while now. Long story short:
I’m looking for a way to, in the first level cache, “reset” a property on an entity each time I do an update or an insert.
What I want to achieve is that the property in question will always be considered to be dirty by NHibernate when using dynamic update or insert.
The backstory for this is that I know that, if the transaction was successful, the column that I want to “reset” will be set to Null in the database by a trigger. On the flip side, the first level cache does not know this, and thus NHibernate will think that the property was not updated when I set it to the same value as I did on the previous update/insert. The catch is that my trigger is dependent on this value being set. The resulting mess is that if I want to use dynamic update or insert I’m only able to update/insert an entity once without “refreshing” it afterwards (which I really don’t want to do).
Tips or help would be much appreciated, because I’ve really hit a wall here
NHibernate provides many places for extension. Among them is the Session IInterceptor. There is documentation with many details:
http://nhibernate.info/doc/nh/en/index.html#objectstate-interceptors
In this case, we can create our custom one, which will be observing our entity (for example Client) and a property which must be updated every time (for example Code). So our implementation could look like this:
public class MyInterceptor : EmptyInterceptor
{
public override int[] FindDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, NHibernate.Type.IType[] types)
{
var result = new List<int>();
// we do not care about other entities here
if(!(entity is Client))
{
return null;
}
var length = propertyNames.Length;
// iterate all properties
for(var i = 0; i < length; i++)
{
var areEqual = currentState[i].Equals(previousState[i]);
var isResettingProperty = propertyNames[i] == "Code";
if (!areEqual || isResettingProperty)
{
result.Add(i); // the index of "Code" property will be added always
}
}
return result.ToArray();
}
}
NOTE: This is just an example! Apply your own logic for checking the dirty properties.
And we have to wrap Session this way:
var interceptor = new MyInterceptor()
_configuration.SetInterceptor(interceptor);
And this is it. While Client is marked as dynamic-update, the property Code will always be set as dirty
<class name="Client" dynamic-update="true" ...
I have two tables Studies and Series. Series are FK'd back to Studies so one Study contains a variable number of Series.
Each Series item has a Deleted column indicating it has been logically deleted from the database.
I am trying to implement a Deleted property in the Study class that returns true only if all the contained Series are deleted.
I am using O/R Designer generated classes, so I added the following to the user modifiable partial class for the Study type:
public bool Deleted
{
get
{
var nonDeletedSeries = from s in Series
where !s.Deleted
select s;
return nonDeletedSeries.Count() == 0;
}
set
{
foreach (var series in Series)
{
series.Deleted = value;
}
}
}
This gives an exception "The member 'PiccoloDatabase.Study.Deleted' has no supported translation to SQL." when this simple query is executed that invokes get:
IQueryable<Study> dataQuery = dbCtxt.Studies;
dataQuery = dataQuery.Where((s) => !s.Deleted);
foreach (var study in dataQuery)
{
...
}
Based on this http://www.foliotek.com/devblog/using-custom-properties-inside-linq-to-sql-queries/, I tried the following approach:
static Expression<Func<Study, bool>> DeletedExpr = t => false;
public bool Deleted
{
get
{
var nameFunc = DeletedExpr.Compile();
return nameFunc(this);
}
set
{ ... same as before
}
}
I get the same exception when a query is run that there is no supported translation to SQL. (
The logic of the lambda expression is irrelevant yet - just trying to get past the exception.)
Am I missing some fundamental property or something to allow translation to SQL? I've read most of the posts on SO about this exception, but nothing seems to fit my case exactly.
I believe the point of LINQ-to-SQL is that your entities are mapped for you and must have correlations in the database. It appears that you are trying to mix the LINQ-to-Objects and LINQ-to-SQL.
If the Series table has a Deleted field in the database, and the Study table does not but you would like to translate logical Study.Deleted into SQL, then extension would be a way to go.
public static class StudyExtensions
{
public static IQueryable<study> AllDeleted(this IQueryable<study> studies)
{
return studies.Where(study => !study.series.Any(series => !series.deleted));
}
}
class Program
{
public static void Main()
{
DBDataContext db = new DBDataContext();
db.Log = Console.Out;
var deletedStudies =
from study in db.studies.AllDeleted()
select study;
foreach (var study in deletedStudies)
{
Console.WriteLine(study.name);
}
}
}
This maps your "deleted study" expression into SQL:
SELECT t0.study_id, t0.name
FROM study AS t0
WHERE NOT EXISTS(
SELECT NULL AS EMPTY
FROM series AS t1
WHERE (NOT (t1.deleted = 1)) AND (t1.fk_study_id = t0.study_id)
)
Alternatively you could build actual expressions and inject them into your query, but that is an overkill.
If however, neither Series nor Study has the Deleted field in the database, but only in memory, then you need to first convert your query to IEnumerable and only then access the Deleted property. However doing so would transfer records into memory before applying the predicate and could potentially be expensive. I.e.
var deletedStudies =
from study in db.studies.ToList()
where study.Deleted
select study;
foreach (var study in deletedStudies)
{
Console.WriteLine(study.name);
}
When you make your query, you will want to use the statically defined Expression, not the property.
Effectively, instead of:
dataQuery = dataQuery.Where((s) => !s.Deleted);
Whenever you are making a Linq to SQL query, you will instead want to use:
dataQuery = dataQuery.Where(DeletedExpr);
Note that this will require that you can see DeletedExpr from dataQuery, so you will either need to move it out of your class, or expose it (i.e. make it public, in which case you would access it via the class definition: Series.DeletedExpr).
Also, an Expression is limited in that it cannot have a function body. So, DeletedExpr might look something like:
public static Expression<Func<Study, bool>> DeletedExpr = s => s.Series.Any(se => se.Deleted);
The property is added simply for convenience, so that you can also use it as a part of your code objects without needing to duplicate the code, i.e.
var s = new Study();
if (s.Deleted)
...
Model #1 - This model sits in a database on our Dev Server.
Model #1 http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b05d8bc1/Model1.png
Model #2 - This model sits in a database on our Prod Server and is updated each day by automatic feeds. alt text http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a980d4/Model2.png
I have written what should be some simple code to sync my feed (Model #2) into my working DB (Model #1). Please note this is prototype code and the models may not be as pretty as they should. Also, the entry into Model #1 for the feed link data (mainly ClientID) is a manual process at this point which is why I am writing this simple sync method.
private void SyncFeeds()
{
var sourceList = from a in _dbFeed.Auto where a.Active == true select a;
foreach (RivWorks.Model.NegotiationAutos.Auto source in sourceList)
{
var targetList = from a in _dbRiv.Product where a.alternateProductID == source.AutoID select a;
if (targetList.Count() > 0)
{
// UPDATE...
try
{
var product = targetList.First();
product.alternateProductID = source.AutoID;
product.isFromFeed = true;
product.isDeleted = false;
product.SKU = source.StockNumber;
_dbRiv.SaveChanges();
}
catch (Exception ex)
{
string m = ex.Message;
}
}
else
{
// INSERT...
try
{
long clientID = source.Client.ClientID;
var companyDetail = (from a in _dbRiv.AutoNegotiationDetails where a.ClientID == clientID select a).First();
var company = companyDetail.Company;
switch (companyDetail.FeedSourceTable.ToUpper())
{
case "AUTO":
var product = new RivWorks.Model.Negotiation.Product();
product.alternateProductID = source.AutoID;
product.isFromFeed = true;
product.isDeleted = false;
product.SKU = source.StockNumber;
company.Product.Add(product);
break;
}
_dbRiv.SaveChanges();
}
catch (Exception ex)
{
string m = ex.Message;
}
}
}
}
Now for the questions:
In Model #2, the class structure for Auto is missing ClientID (see red circled area). Now, everything I have learned, EF creates a child class of Client and I should be able to find the ClientID in the child class. Yet, when I run my code, source.Client is a NULL object. Am I expecting something that EF does not do? Is there a way to populate the child class correctly?
Why does EF hide the child entity ID (ClientID in this case) in the parent table? Is there any way to expose it?
What else sticks out like the proverbial sore thumb?
TIA
1) The reason you are seeing a null for source.Client is because related objects are not loaded until you request them, or they are otherwise loaded into the object context. The following will load them explicitly:
if (!source.ClientReference.IsLoaded)
{
source.ClientReference.Load();
}
However, this is sub-optimal when you have a list of more than one record, as it sends one database query per Load() call. A better alternative is to the Include() method in your initial query, to instruct the ORM to load the related entities you are interested in, so:
var sourceList = from a in _dbFeed.Auto .Include("Client") where a.Active == true select a;
An alternative third method is to use something call relationship fix-up, where if, in your example for instance, the related clients had been queried previously, they would still be in your object context. For example:
var clients = (from a in _dbFeed.Client select a).ToList();
The EF will then 'fix-up' the relationships so source.Client would not be null. Obviously this is only something you would do if you required a list of all clients for synching, so is not relevant for your specific example.
Always remember that objects are never loaded into the EF unless you request them!
2) The first version of the EF deliberately does not map foreign key fields to observable fields or properties. This is a good rundown on the matter. In EF4.0, I understand foreign keys will be exposed due to popular demand.
3) One issue you may run into is the number of database queries requesting Products or AutoNegotiationContacts may generate. As an alternative, consider loading them in bulk or with a join on your initial query.
It's also seen as good practice to use an object context for one 'operation', then dispose of it, rather than persisting them across requests. There is very little overhead in initialising one, so one object context per SychFeeds() is more appropriate. ObjectContext implements IDisposable, so you can instantiate it in a using block and wrap the method's contents in that, to ensure everything is cleaned up correctly once your changes are submitted.