How to make a return type for a result set in LINQ - sql

I am having a problem determining how c# and LINQ solve the common problem of handling a data structure that does not necessarily return a table structure, but instead a resultset.
I have a stored procedure that works, and have included it in my DBML
[Function(Name="dbo.p_GetObject")]
public int p_GetObject([Parameter(Name="ObjectType", DbType="NVarChar(200)")] string objectType, [Parameter(Name="ItemState", DbType="Bit")] System.Nullable<bool> itemState, [Parameter(Name="IsPublished", DbType="Bit")] System.Nullable<bool> isPublished)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), objectType, itemState, isPublished);
return ((int)(result.ReturnValue));
}
The dbml says that the return type is (None) and this could be the crux issue. However I don't have a DBML object that matches the resultset.
The SP takes three parameters, and returns a result set with three columns (ID, Name, Value) with multple rows. I can create a data object for this, and call it resultSet
When I write a function call for this, I get stuck:
public List<resultset> GetObject(string objectType, bool itemState, bool isPublished)
{
MyDataContext.p_GetObject(objectType, itemState, isPublished);
}
My questions are:
how do I have the data context call to the stored procedure populate my resultSet object? Is there a better approach? What should the return type be? A SQL view? Looking for good suggestions...

If it simply isn't understanding your SP, that could be the SET FMT_ONLY issue... try generating the data from a simplified version of the SP?
Normally, SPs / UDFs that don't map 1:1 with an existing entity would expose themselves in a generated type. You can rename this in the DBML file (not in the designer), but personally I wouldn't; I tend to mark the SP as private, and write my own method that projects into my own POCO type (defined for the repository):
var typed = from row in cxt.SomeFunction(123)
select new MyType {Id = row.Id, Name = row.Name, ...}
The reason for this is partly for repository purity, and partly to guard against the designer's habit of re-writing the DBML in unexpected ways ;-p See here for more.

Related

How to update two columns with same name from two tables in a join query

I am getting an error:
Property or Indexer cannot be assigned to "--" it is read only
when trying to update two columns with the same name in two tables in a join query. How do I get this to work? Thanks!
The anonymous object created in your projection ("select new" part) is read-only and its properties are not tracked by data context by any means.
Instead, you can try this:
//...
select new
{
p1 = p,
p2 = t
}
foreach (var row in updates)
{
row.p1.Processed = true;
row.p2.Processed = true;
}
In order to improve performance you may also want to take a look at batch update capabilities of Entity Framework Extensions (if you are using Entity Framework): https://entityframework-extensions.net/overview
Yes, that's due to anonymous type properties are read only, from documentation:
Anonymous types provide a convenient way to encapsulate a set of
read-only properties into a single object without having to explicitly
define a type first.
I suggest you to create a custom class with the two entities you need (a DTO):
public class PassengerDTO
{
public Passenger Passenger {get;set}
public PassengerItinerary PassengerItinerary {get;set}
}
And use it in your projection, You need the entity instances and not just the properties you want to modify because, when you modify the Processed property in the foreach the proxy class that represent your entity is going to change the status of you entity to Updated.

Does CF ORM have an Active Record type Update()?

Currently I am working partly with cfwheels and its Active Record ORM (which is great), and partly raw cfml with its Hibernate ORM (which is also great).
Both work well for applicable situations, but the thing I do miss most when using CF ORM is the model.update() method that is available in cfwheels, where you can just pass a form struct to the method, and it will map up the struct elements with the model properties and update the records.. really good for updating and maintaining large tables. In CF ORM, it seems the only way to to update a record is to set each column individually, then do a save. Is this the case?
Does cf9 ORM have an Active Record type update() (or equivalent) method which can just receive a struct with values to update and update the object without having to specify each one?
For example, instead of current:
member = entityLoadByPK('member',arguments.id);
member.setName(arguments.name);
member.setEmail(arguments.email);
is there a way to do something like this in CF ORM?
member = entityLoadByPK('member',arguments.id);
member.update(arguments);
Many thanks in advance
In my apps I usually create two helper functions for models which handle the task:
/*
* Get properties as key-value structure
* #limit Limit output to listed properties
*/
public struct function getMemento(string limit = "") {
local.props = {};
for (local.key in variables) {
if (isSimpleValue(variables[local.key]) AND (arguments.limit EQ "" OR ListFind(arguments.limit, local.key))) {
local.props[local.key] = variables[local.key];
}
}
return local.props;
}
/*
* Populate the model with given properties collection
* #props Properties collection
*/
public void function setMemento(required struct props) {
for (local.key in arguments.props) {
variables[local.key] = arguments.props[local.key];
}
}
For better security of setMemento it is possible to check existence of local.key in variables scope, but this will skip nullable properties.
So you can make myObject.setMemento(dataAsStruct); and then save it.
There's not a method exactly like the one you want, but EntityNew() does take an optional struct as a second argument, which will set the object's properties, although depending on how your code currently works, it may be clunky to use this method and I don;t know whether it'll have any bearing on whether a create/update is executed when you flush the ORM session.
If your ORM entities inherit form a master CFC, then you could add a method there. Alternatively, you could write one as a function and mix it into your objects.
I'm sure you're aware, but that update() feature can be a source of security problems (known as the mass assignment problem) if used with unsanitized user input (such as the raw FORM scope).

WCF returning results from stored procedure

I have been tasked with implementing a WCF service in VB.NET. This WCF service will be consumed by our own .NET Windows Forms application and provide data from a SQL Server database. The data will be displayed in a third party grid (Infragistics).
Originally I was going to use ADO.NET and return a datatable or dataset from SQL within the WCF service but I have read too many articles encouraging me to stay away from returning datatables over the internet. I am not worried about the interoperability but I am worried about the size/speed of the process. This had lead me to start using linq to sql to return entities which I can pass over the internet (.dbml)
The problem:
A lot of our data comes from stored procedures, these stored procedures return result sets that are a mix-match of existing tables and therefore does not match any return type. I have imported a stored procedure that returns (Auto-generated Type) however when I try and return this type over the WCF service I get this error in the tracelog:
Type
System.Data.Linq.SqlClient.SqlProvider+SingleResult'1[benchmark_prd_colour_findResult]
cannot be serialized. Consider marking it with the
DataContractAttribute attribute, and marking all of its members you
want serialized with the DataMemberAttribute attribute. If the type
is a collection, consider marking it with the
CollectionDataContractAttribute. See the Microsoft .NET Framework
documentation for other supported types.
Please let me know what I need to be doing to be able to return the results of a stored procedure when the results don't match any particular existing table
Translate it to VB! (Sorry, didnt see the tag... Assumed C#)
Build your own object - and mark this up like this:
[DataContract]
public class MyObject{
//here go all your fields which match whats returned in your stored proc
[DataMember]
public String MyField;
[DataMember]
public int MySecondField;
}
Then populate this when getting the result of your stored proc
public MyObject GetResult(){
stored_proc_result result = factory.callProc(); //obviously replace this with whatever you use!
MyObject obj = new MyObject{
MyField = FieldOne,
MySecondField = FieldTwo
};
return obj;
}
Then if you pass the MyObject instance in your WCF call it should work.

Restricting an NHibernate query using ICriteria according to an enumeration of enums

I have an entity, with a field of type enum, that is persisted as an integer in my database.
When retrieving objects from the database using ICriteria, I wish to restrict the results to those with the field being a member of a collection of enum values. Does Restrictions.In work with a collection of enums?
The following does not work. Do I have to perform something like type-casting at the "restrictions.in" part of the query?
var myEnumCollection = new MyEnum[] { MyEnum.One };
return FindAll<MyType>(Restrictions.In("EnumProperty", myEnumCollection));
FindAll is a method encapsulating
criteria.GetExecutableCriteria(Session).List<MyType>()
My initial guess would be that you'll need to compare against the integer values of the enum members (assuming that you're mapping the enum as an integer); so something like:
var myEnumCollection = new int[] { MyEnum.One };
return FindAll<MyType>(Restrictions.In("EnumProperty", myEnumCollection));
May be the solution that you're after. If you update your post with further details (mapping of the enum member and the sql being generated by the query), I may be able to provide further assistance.

How do I run an HqlBasedQuery that returns an unmapped list of objects using nHibernate?

I want to run a query against two tables (which happen to be mapped in ActiveRecord). The query returns a result list that cannot be mapped to an ActiveRecord object (as it is custom aggregate information).
For instance
Dim query_str as string = "Select distinct d.ID, (select count(1) as exp from Sales_Leads where date_created <= :todays_date) as NbrLeads from Dealer d"
Dim q As Queries.HqlBasedQuery = New Queries.HqlBasedQuery(GetType(ICollection), query_str)
q.SetParameter("todays_date", DateTime.Today)
Dim i As ICollection = ActiveRecordMediator.ExecuteQuery(q)
What I'm looking for is simple execution of SQL, without an ActiveRecord object returned.
So, ideally, I'd be able to look at i("NbrResults") for each item in the collection.
The error I am getting is:
You have accessed an ActiveRecord
class that wasn't properly
initialized. The only explanation is
that the call to
ActiveRecordStarter.Initialize()
didn't include
System.Collections.ICollection class
Well, this was asked a long time ago but I have a working answer.
public static IList<T> ExecuteQuery<T>(HqlBasedQuery query)
where T : new()
{
query.SetResultTransformer(new NHibernate.Transform.AliasToBeanResultTransformer(typeof(T)));
var results = (ArrayList)ActiveRecordMediator.ExecuteQuery(query);
List<T> list = new List<T>(results.Count);
for (int i = 0; i < results.Count; i++)
{
list.Add((T)results[i]);
}
return list;
}
This will give you back results of type T. Type T can be anything you want. Type T needs a no argument constructor and it needs public fields or properties that match the column names or aliases in the query you build.
We do this all the time. Particularly when you want to use aggregate function in HQL to produce aggregate data.
A companion function will allow you to just pass in your query as a string as well as any positional parameters you might have:
public static IList<T> ExecuteQuery<T, U>(string hqlQuery, params object[] parameters)
where T : new()
{
return ExecuteQuery<T>(new HqlBasedQuery(typeof(U), hqlQuery, parameters));
}
Type U is any type that is a valid ActiveRecord type. It doesn't even have to be one of the types you are referencing. If you want you could replace it will some type you know is gonna be valid int he session and ditch the extra parameter.
Here was my final solution:
Dim query_str As String = "SELECT DISTINCT d.ID, count( l ) from LEAD as l join l.Dealer as d where l.DateCreated >= DATEADD(day, -30, :todays_date) GROUP BY d.ID"
Then obtain the active record session (or NHibernate, still don't even know what is returned here):
Dim sess As ISession = activerecordmediator.GetSessionFactoryHolder().CreateSession(GetType(ActiveRecordBase))
Dim q As NHibernate.IQuery = sess.CreateQuery(query_str)
q.SetParameter("todays_date", DateTime.Today)
Dim i As IList = q.List() ' get results
In the .aspx page, the result can be accessed in a GridView like so:
You're stepping outside the NHibernate paradigm to call down to SQL, which is somewhat against the spirit of ORM. It's not 'bad' per-se, but I'd certainly avoid breaking the abstraction if I could to try to maintain a looser coupling.
You can do what you want with a straight HQL query, which will return a collection of tuples containing the results of your query. I explained how to do this here
Custom query with Castle ActiveRecord
which you might want to have a look at. Even though you must specify a type when you new an HQLBasedQuery, NH is smart enough to know that if you don't select a type instance it should assemble a result-set based on object tuples.
(IMNSHO it's still a bit non-pure - I'd be tempted to try to model this relationship as an object and map it accordingly, but then I'd have to bash the DB into shape to suit the object model and that's not going to fly in all cases.)