NHibernate one-to-many removing related records - c#

I have three tables with the following structure:
Todo
Id
Name
User
Id
Name
TodoUser
Id
TodoId
UserId
Status
For Todo.ResponsibleUsers I have created the following mapping:
private IList<TodoUser> responsibleUsers = new List<TodoUser>();
[Bag(0, Name = "ResponsibleUsers", Cascade = CascadeStyle.AllDeleteOrphan, Table = "TodoUser", Inverse = true)]
[Key(1, Column = "TodoId")]
[OntToMany(2, ClassType = typeof(TodoUser))]
public virtual IList<TodoUser> ResponsibleUsers {
get { return responsibleUser; }
set { responsibleUsers = (IList<TodoUser>)value; }
}
Does the property ResponsibleUsers have to be of type IList<TodoUser> or can it also be of type List<TodoUser>?
I would like to do something like todo.ResponsibleUsers.RemoveAll(itemsToRemove); which is not possible on an IList<TodoUser>

To answer the question:
Does the property ResponsibleUsers have to be of type IList<TodoUser> or can it also be of type List<TodoUser>?
Check the doc:
Chapter 6. Collection Mapping
6.1. Persistent Collections
NHibernate requires that persistent collection-valued fields be
declared as a generic interface type, for example:
public class Product
{
private string serialNumber;
private ISet<Part> parts = new HashSet<Part>();
public ISet<Part> Parts
{
get { return parts; }
set { parts = value; }
}
public string SerialNumber
{
get { return serialNumber; }
set { serialNumber = value; }
}
}
The actual interface might be
System.Collections.Generic.ICollection<T>,
System.Collections.Generic.IList<T>,
System.Collections.Generic.IDictionary<K, V>,
System.Collections.Generic.ISet<T>
So, yes, we must use interface.
To solve the issue with
I would like to do something like todo.ResponsibleUsers.RemoveAll(itemsToRemove);
We can implement some custom extension method as
public static class Ext
{
public static void RemoveAll(this IList<T> list, IEnumerable<T> toRemove)
{
... // remove items matching toRemove from the list
}

Related

Method with generic return type but not generic input. Is this possible?

Suppose we have a NodeData class:
public class NodeData<T>
{
public string Name;
public T Value;
public NodeData(string name, T value)
{
this.Name = name;
this.Value = value;
}
}
And a base Node class and child classes that have several properties with type NodaData:
public class Node
{
public List<NodeData<T>> listOutputs<T>()
{
var fieldInfos = GetType().GetFields();
var list = new List<NodeData<T>>();
foreach (var item in fieldInfos)
{
Type t = item.FieldType;
string name = item.Name;
if (t == typeof(NodeData<T>))
{
var output = new NodeData<T>(name, default(T));
list.Add(output);
}
}
return list;
}
}
public class TestNode : Node {
public NodeData<int> data;
public NodeData<double> data2;
public NodeData<double> data3;
public TestNode ()
{
data = new NodeData<int>("test", 111);
data2 = new NodeData<double>("test", 113);
}
}
As you can see there is a method which lists all outputs with type T in the Node class So I can find what are the output fields of the child class in runtime:
TestNode node = new TestNode ();
var list = node.listOutputs<int>(); // this returns data
But I need to know how to use this method to list all NodeOutputs of any type T. In this example int and double. Do I need to add a method with this signature public List<NodeData<T>> listOutputs() // should return all properties data, data2, data3. Is it possible to have method like this? return type is generic but there is no type argument for method.
Even after your edit(s) it is not entirely clear what you are trying to achieve but here are my assumptions:
-You want to have some kind of Node object that acts as a container for different types of NodeData elements.
-You want to be able to return one list from this Node object that contains all NodeData elements stored in the Node container, regardless of the NodeData objects' type.
Instead of returning a List> object from the listOutputs methods, just return the non-generic version of the List object. Then you don't have to deal with T in the method call.
The logic that loops through the objects in the non-generic list can then examine the type to process the contained NodeData objects correctly.
Important note: My proposed solution is by no means pretty but I think it answers the question. In my opinion something is already seriously flawed from an OO point of view in the presented code (e.g. use of reflection) and a better solution would have to start by changing the underlying data structures. But that can only be done if we have more information how this is to be used, e.g. what kind of logic consumes the returned list.
You can create a base interface that will be used to return the generic data.
public interface INodeData
{
string Name { get; }
}
public class NodeData<T> : INodeData
{
public string Name { get; private set; }
public T Value { get; private set; }
public NodeData(string name, T value)
{
this.Name = name;
this.Value = value;
}
}
I modified the function to return a list of the interface. Doing this you won't depend on T.
public class Node
{
public List<INodeData> listOutputs()
{
var fieldInfos = GetType().GetFields();
var list = new List<INodeData>();
foreach (var item in fieldInfos)
{
INodeData data = GetType().GetField(item.Name).GetValue(this) as INodeData;
list.Add(data);
}
return list;
}
}
If you test the method, it should return the fields in a list. To work with a specific type, you can make use of is before using the type you search for.
public class TestNode : Node
{
public NodeData<int> data;
public NodeData<double> data2;
public NodeData<double> data3;
public TestNode()
{
data = new NodeData<int>("test", 111);
data2 = new NodeData<double>("test", 113);
}
}
private static void Main(string[] args)
{
TestNode node = new TestNode();
var list = node.listOutputs(); // this returns data
}
This may well be an XY problem, in that you probably want to rethink how you are designing your classes because using reflection in this way doesn't seem right. But give the problem you've presented, I'd tackle it like this:
public abstract class NodeDataBase
{
public string Name { get; set; }
public NodeData(string name)
{
this.Name = name;
}
// this isn't actually needed, but might be helpful
public abstract object GetValue();
}
public class NodeData<T> : NodeDataBase
{
public T Value { get; set; }
public NodeData(string name, T value) : base(name)
{
this.Value = value;
}
public override object GetValue()
{
return Value;
}
}
And now your method signature would be:
public List<NodeDataBase> listOutputs()
And with the list returned, you can use the GetValue method to get the actual values without needing to cast to the right generic type to be able to get at the Value property.
You could also just have a return type of List<object>, but then you'll have to cast each member of that list to the right generic type before you can access it's properties.
You can also avoid that nasty reflection code, instead of having data, data1, and data2, you could simply do this in your Node class:
public class Node
{
public List<NodeDataBase> Data { get; protected set; }
public Node()
{
Data = new List<NodeDataBase>();
}
}
And now you don't even need your listOutputs method because you can just get the list from the node (unless you actually wanted a copy, but that's fairly trivial to implement).
And you TestNode would be just:
public class TestNode : Node {
public TestNode ()
{
Data.Add(new NodeData<int>("test", 111));
Data.Add(new NodeData<double>("test", 113));
}
}

Sort the list in a class that is requested from the database

I have a class that contains a list of parameters. For example:
public class Container
{
public List<Parameter> Parameters { get; set; }
}
public class Parameter
{
puplic string Name {get; set;}
}
Class Сontainer obtained from the database through Entity Framework. Many classes contain Container. I need to ensure that all classes that contain Сontainer and also retrieved from the database containing the sorted list of Parameters. That is, the Container must sort Parameters or request step or immediately thereafter.
How this can be achieved?
Maybe write to the configuration
internal class ContainerConfiguration : EntityTypeConfiguration<Container>
{
public ContainerConfiguration()
{
ToTable("Container");
HasKey(p => p.Id);
... ???
}
}
Or wright in dataSet
protected override IQueryable<Container> DataSet(DbContext db)
{
return db.Set<ProcessMeasurer>()
.Include(it => it.Parameters.Select(p => p.Parameter));
}
Another option for solving the problem:
Create your attribute and specify which field to use for sorting by default:
public class DefaultOrderFieldAttribute : Attribute
{
public DefaultOrderFieldAttribute()
{
}
public string FieldName { get; set; }
}
[DefaultOrderField(FieldName = "ParameterName")]
public partial class Parameter
{
}
Write a Visitor, which in the case of detection of our attribute modifies select:
public class DefaultOrderVisitor : DefaultExpressionVisitor
{
public override DbExpression Visit(DbScanExpression expression)
{
const string NAMESPACE = "OrderTest";
var type =
Assembly.GetExecutingAssembly().GetType(string.Format("{0}.{1}", NAMESPACE, expression.Target.Name));
var attribute =
type.GetCustomAttributes(typeof (DefaultOrderFieldAttribute)).SingleOrDefault() as
DefaultOrderFieldAttribute;
if (attribute != null)
return expression.OrderBy(ex => ex.Property(attribute.FieldName));
return expression;
}
}
Put in our Visitor Interceptor:
public class DefaultOrderInterceptor : IDbCommandTreeInterceptor
{
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace == DataSpace.SSpace)
{
var queryCommand = interceptionContext.Result as DbQueryCommandTree;
if (queryCommand != null)
{
var newQuery = queryCommand.Query.Accept(new DefaultOrderVisitor());
interceptionContext.Result = new DbQueryCommandTree(queryCommand.MetadataWorkspace,
queryCommand.DataSpace, newQuery);
}
}
}
}
and register it in the configuration (this class just has to be in the same assembly as the model):
public class EntityFrameworkConfiguration : DbConfiguration
{
public EntityFrameworkConfiguration()
{
AddInterceptor(new DefaultOrderInterceptor());
}
}
Need to work with the entity class.
If we want to sort the collection was in all the elements that comprise it, we have to change the appropriate property.
Obvious variant - creating property setter.
private List<Parameter> _parameters;
public List<Parameter> Parameters
{
get { return _parameters; }
set { _parameters = value.OrderBy(...).ToList();
}
But the behavior of the compiler (call the setter once, and the multiple callin to the getter) gave me a reason to assume that the target collection is not put in a property all at once. The items in the query is gradually added to the collection. Therefore, sorting in setter does not always work.
Therefore, we must carry out sorting the return value
get
{
if(_parameters == null) return null;
_parameters = _parameters.OrderBy(...).ToList();
return _parameters;
}
It works. But the problem is that an appeal to the getter, and hence sorting, will be carried out when EntityFramework inserts each value. This affects the performance.
The best variant that I know at the moment is to inherit all entities from the interface with the function Prepare
public interface IEntity
{
void Prepare();
}
and implement it in each class model. Models that comprise other models cause a method to prepare, for each desired properties.
public class SomeModel : IEntity
{
public CustomType SomeProperty { get; set; }
public OneMoreCustomType AnotherProrerty { get; set; }
public void Prepare()
{
SomeProperty.Prepare();
AnotherProperty.Prepare();
}
}
For the respective classes it will take appropriate action. Including sorting.
Сall a method to prepare the Сontainer (in this case) you before using.
For example, in the Business Logic (MVPVM).

Dynamic linq enums filtering error

I trying to use dynamic linq for runtime datagrid filtering with DataGridFiltering project.but i have a problem with enums.
for example, i have a class which contain an enum property like this :
public class Student
{
public Student(int id,string name,StudentType type)
{
Id = id;
Name = name;
Type = type;
}
public int Id { get; set; }
public string Name { get; set; }
public StudentType Type { get; set; }
}
and StudentType enum is :
public enum StudentType : byte
{
Normal=0,
Good
};
i create a controller class for work with list of students.
in my controller i have a method to find students by type.
this is FindByType Method:
public IList<Student> FindByType(string type)
{
return _students.AsQueryable().Where("Type.ToString().StartWith(#0)",type).ToList();
}
when i call FindByType Method i get this error in ParseMemberAccess method of dynamic linq:
Methods on type 'Enum' are not accessible
I think the problem is that the dynamic linq library you are using does not support any Enum methods, such as Enum.Equals(otherVal), or Enum.ToString(). One way to get around this, if you have to use dynamic-linq is :
public IList<Student> FindByType(StudentType type)
{
return _students.AsQueryable().Where("Type = (#0)", type).ToList();
}
However, if you are able to use standard linq, and you really want to pass in a string for some reason, something like this is much cleaner:
public IList<Student> FindByType(string type)
{
return _students.Where(s => s.Type.ToString().StartsWith(type)).ToList();
}
Edit :
If you need the ability to search using StartsWith and you are not allowed to use the standard linq query above, here is something that will give the same result with alot more code
public IList<Student> FindByType(string type)
{
//Replace e.StartsWith with whatever method you wish to filter by
var studentTypeNames =typeof(StudentType).GetEnumNames().Where(e => e.StartsWith(type)).ToList();
var students = new List<Student>();
foreach (var studentTypeName in studentTypeNames)
{
StudentType studentType;
Enum.TryParse(studentTypeName, true, out studentType);
students.AddRange(_students.AsQueryable().Where("Type = (#0)", studentType).ToList());
}
return students;
}
In Dynamic Linq you cann't call methods from class that not in predefined classes array, for workaround you may add property in Student class like this:
public string StudentTypeString {get {return Type.ToString(); } }
and use next query
public IList<Student> FindByType(string type)
{
return _students.AsQueryable().Where("StudentTypeString.StartWith(#0)",type).ToList();
}

Accessing C# property name or attributes

I would like to automatically generate SQL statements from a class instance. The method should look like Update(object[] Properties, object PrimaryKeyProperty). The method is part of an instance (class, base method - generic for any child). Array of properties is an array of class properties, that will be used in update statement. Property names are equal to table field names.
The problem is that I can't get property names.
Is there any option to get a property name inside class instance?
sample:
public class MyClass {
public int iMyProperty { get; set; }
public string cMyProperty2 { get; set; }
{
main() {
MyClass _main = new MyClass();
_main.iMyProperty.*PropertyName* // should return string "iMyProperty"
{
I am aware of PropertyInfo, but I don't know hot to get the ID of a property from GetProperties() array.
Any suggestion?
Just wrote an implementation of this for a presentation on lambdas for our usergroup last Tuesday.
You can do
MembersOf<Animal>.GetName(x => x.Status)
Or
var a = new Animal()
a.MemberName(x => x.Status)
the code:
public static class MembersOf<T> {
public static string GetName<R>(Expression<Func<T,R>> expr) {
var node = expr.Body as MemberExpression;
if (object.ReferenceEquals(null, node))
throw new InvalidOperationException("Expression must be of member access");
return node.Member.Name;
}
}
Link to the presentation and code samples.
Also in SVN (more likely to be updated): http://gim-projects.googlecode.com/svn/presentations/CantDanceTheLambda
I found a perfect solution in This Post
public static string GetPropertyName<T>(Expression<Func<T>> propertyExpression)
{
return (propertyExpression.Body as MemberExpression).Member.Name;
}
And then for the usage :
var propertyName = GetPropertyName(
() => myObject.AProperty); // returns "AProperty"
Works like a charm
You can do something like this:
Type t = someInstance.getType();
foreach (MemberInfo mi in t.GetMembers())
{
if (mi.MemberType == MemberTypes.Property)
{
Console.WriteLine(mi.Name);
}
}
to get all the property names for instance's type.
You can get the name (I assume that's what you meant by ID) of a property using PropertyInfo.Name. Just loop through the PropertyInfo[] returned from typeof(className).GetProperties()
foreach (PropertyInfo info in typeof(MyClass).GetProperties())
{
string name = info.Name;
// use name here
}
Since you already have an explicit handle to the specific property you want, you know the name - can you just type it?
Not 100% sure if this will get you what you're looking for, this will fetch all properties with [Column] attribute inside your class:
In the datacontext I have:
public ReadOnlyCollection<MetaDataMember> ColumnNames<TEntity>( )
{
return this.Mapping.MappingSource.GetModel(typeof(DataContext)).GetMetaType(typeof(TEntity)).DataMembers;
}
Fetching the table column-names that are properties inside the class:
MyDataContext db = GetDataContext();
var allColumnPropertyNames = db.ColumnNames<Animal>().Where(n => n.Member.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false).FirstOrDefault() != null).Select(n => n.Name);
Let's say (from the first sample, method update of a class MyClass):
public class MyClass {
public int iMyStatusProperty { get; set; }
public int iMyKey { get; set; }
public int UpdateStatusProperty(int iValue){
this.iMyStatusProperty = iValue;
return _Update( new[iMyStatusProperty ], iMyKey); // this should generate SQL: "UPDATE MyClass set iMyStatusProperty = {iMyStatusProperty} where iMyKey = {iMyKey}"
}
{iMyStatusProperty} and {iMyKey} are property values of a class instance.
So, the problem is how to get property name (reflection) from a property without using names of properties as strings (to avoid field name typos).

Is it possible to have a List<string> as a property on an active record class

Is it possible to have a HasMany relationship of a basic type such as String, on an ActiveRecord class, without the need for creating another entity such as (TodoListItem) to hold the value.
[ActiveRecord]
public class TodoList
{
[PrimaryKey]
public int Id
{
get { return _id; }
set { _id = value; }
}
[HasMany(typeof(string)]
public IList<string> Items
{
get { return _items; }
set { _items= value; }
}
}
Can anyone help?
Yes, you can do this. You can map a one-to-many relation to a built-in or simple type (value type or string) rather than a persisted type.
You'll need to specify the ColumnKey, Table and Element params in the HasMany attribute declaration to get it to wire up properly. You have to have a surrogate key column so the AR can handle updates and cascades, and then Element tells AR which column in the table holds the simple value it will use to make the list.
[HasMany(typeof(string), Table="ToDoList_Items",
ColumnKey = "ListItemID", Element = "Item")]
public IList<string> Items { get; set; }
(or something similar - I haven't got a compiler handy on this box to check it; but per the API docs it ought to work.)
Speaking of which, if you haven't already had a look, http://api.castleproject.org is kinda indispensible for any work with the Castle stack.
In ActiveRecord, your types map to a record in a table (by default). It seems like you are confusing how this type should map to your table.
The MyClass type should have a definition something like this (excluding the PK settings):
[ActiveRecord(Table = "MyTable")]
public class MyClass : ActiveRecordBase<MyClass>
{
[Property]
public int Id { get; set; }
[Property]
public int MyClassId { get; set; }
[Property]
public string ListItem { get; set; }
}
Then, to load the list:
public void LoadMyClasses()
{
MyClass[] results = MyClass.FindAll();
}
I'd suggest you spend some time with the ActiveRecord documentation (or tutorial) as that should also help clear up any confusion.

Categories