NHibernate - IUserType For Changing Type? - nhibernate

My application has the following classes:
public class Widget {
public virtual int Id { get; set; }
public virtual WidgetType Type { get; set; }
public virtual string Parameters { get; set; }
}
public class WidgetType {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string ParametersAssembly { get; set; }
public virtual string ParametersClass { get; set; }
}
Now if i'd like to update the Parameters for a particular widget i would say something like:
// Get the widget
var widget = GetWidget(1);
// Create an instance of the type parameters class
var parameters = Activator.CreateInstance(Assembly.LoadFrom(Server.MapPath("~/bin/"
+ widget.Type.ParametersAssembly + ".dll")).GetType(widget.Type.ParametersClass));
... Code here to update the parameters
widget.Parameters = new XmlSerializer(parameters.GetType()).Serialize(parameters);
I have to do the reverse when i wish to get the parameters. You can imagine this becomes quite tedious. I was wondering if it was possibly to automatically do this?
I've been looking at the IUserType interface. I found an article which is kind of similar. However my problem is a little more complicated as my type changes based on the type of the widget.
I'd appreciate it if someone could let me know if this is possible and possibly how it could be achieved. Thanks

an easy way
public class Widget
{
public virtual int Id { get; set; }
public virtual WidgetType Type { get; set; }
private string _serializedParameters;
private virtual string SerializedParameters {
get
{
return new XmlSerializer(Parameters.GetType()).Serialize(Parameters);
}
set
{
_serializedParameters = value;
// code to deserialize the Parameters and set to Parameters
var ptype = Assembly.LoadFrom(Server.MapPath("~/bin/" + widget.Type.ParametersAssembly + ".dll")).GetType(widget.Type.ParametersClass);
Parameters = Activator.CreateInstance(ptype);
}
}
private object _parameters;
public virtual object Parameters
{
get
{
if (_parameters == null)
_parameters = Activator.CreateInstance(Assembly.LoadFrom(Server.MapPath("~/bin/" + widget.Type.ParametersAssembly + ".dll")).GetType(widget.Type.ParametersClass));
return _parameters;
}
set { _parameters = value; }
}
}
it can't be in the Parameters property because then you have to get -> alter -> set instead of get -> alter. But you are right that building the parameters object should go in the getter of Parameters because only there we can be sure to have the WidgetType loaded
it is essentially the same as a UserType except that we know that WidgetType is there

Related

Can not get list in a model which I get it AsQuerable in ef?

public class Product
{
public int Id{get;set;}
public int UserId{get;set;}
public Users User{get;set;}
}
I have set the Users to Product's relative:
b.HasOne("User").WithMany().HasForeignKey("UserID");
When I use entityframework to get the list of products.
The User is returned null, why?
There is a value in User table and the UserId is right in Product Table.
var list = _context.Products.AsQueryable();
the items in list has the User=null.
You need to Include the entity you're looking for. For example, let's suppose I have the following context.
AppDbContext.cs
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Notification> Notifications { get; set; }
public DbSet<Offer> Offers { get; set; }
}
Notification.cs
public class Notification
{
public int Id { get; set; }
public string Title { get; set; }
public int? OfferId { get; set; }
public virtual Offer Offer { get; set; }
}
If you want to use the Offer entity from Notification, you need to use the following statement:
context.Notifications.Include(n=> n.Offers).ToList();
// Your code goes here
In your situation:
var list = _context.Products.Include(p=> p.User).AsQueryable();
You have to explicitly ask to include the users in the returned list.
_context.Products.Include(p => p.Users).AsQueryable();

How to map object type field in Nhibernate Auto Mapping

When trying to map, I got this error:
Association references unmapped class: System.Object
My class:
public partial class MessageIdentifier
{
public virtual int ID { get; set; }
public virtual object Item { get; set; }
}
And the convention:
public class MyUsertypeConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
if (instance.Type.Name == "Object")
instance.CustomType<string>();
}
}
Kindly suggest how to map?
As a simple (quick, naive) solution - I would suggest to create and map real string property. And then let your setter and getter (or some AOP or listener) to do the "to/from string conversion":
public partial class MessageIdentifier
{
public virtual int ID { get; set; }
public virtual object Item
{
get { return ... my conversion from string; }
set { ItemString = ...my conversion to string; }
}
public virtual string ItemString { get; set; }
}
A smart and preferred (but a bit more challenging) is to create CustomType - which will hide that conversion and support REUSE. Check e.g. here
NHibernate Pitfalls: Custom Types and Detecting Changes
Creating and Testing a Custom NHibernate User Type
Not a satisfactory answer. It doesn't work with class that is generated from xsd by using XML. You can try the following:
public partial class MessageIdentifier
{
public virtual int ID { get; set; }
private object itemField;
public object Item
{
get { return this.itemField; }
set { this.itemField = value; }
}
}

Using ReadAsAsync<T>() to deserialize complex Json object

I want to use ReadAsAsync() in my mvc project with .net 4.0. The result comes as null.
If I enter the uri to address bar, the result in chrome as(tag names are changed):
<ns2:MyListResponse xmlns:ns2="blablabla">
<customerSessionId>xxcustomerSessionIdxx</customerSessionId>
<numberOfRecordsRequested>0</numberOfRecordsRequested>
<moreResultsAvailable>false</moreResultsAvailable>
<MyList size="1" activePropertyCount="1">
<MySummary order="0">
<id>1234</id>
<name>...</name>
.
.
</MySummary>
</MyList>
</ns2:MyListResponse>
If I use the statement in code :
using (var client = new HttpClient())
{
var response = client.GetAsync(apiUri).Result;
var message = response.Content.ReadAsStringAsync().Result;
var result1 = JsonConvert.DeserializeObject<MyListResponse>(message);
var result2 = response.Content.ReadAsAsync<MyListResponse>().Result;
}
the message comes in string format as "{\"MyListResponse\":{\"customerSessionId\"...}" which corresponds to a json object as:
{"MyListResponse":
{"customerSessionId":"xxcustomerSessionIdxx",
"numberOfRecordsRequested":0,
"moreResultsAvailable":false,
"MyList":
{"#size":"1",
"#activePropertyCount":"1",
"MySummary":
{"#order":"0",
"id":1234,
"name":"...",
.
.
}
}
}
}
and the properties of result1 and result2 came as null or default values. Class definitions are below. I want to read the content as an object but I couldn't. What do you advice to solve this? What am I doing wrong? Thanks in advance.
public class MySummary
{
public int #Order { get; set; }
public string Id { get; set; }
public string Name { get; set; }
.
.
}
public class MyList
{
public int #Size { get; set; }
public int #ActivePropertyCount { get; set; }
public MySummary MySummary{ get; set; }
}
public class MyListResponse
{
public string CustomerSessionId { get; set; }
public int NumberOfRecordsRequested { get; set; }
public bool MoreResultsAvailable { get; set; }
public MyList MyList { get; set; }
}
I defined a new class as:
public class ResponseWrapper
{
public MyListResponse MyListResponse { get; set; }
}
then I used this wrapper with,
var result1 = JsonConvert.DeserializeObject<ResponseWrapper>(message);
var result2 = response.Content.ReadAsAsync<ResponseWrapper>().Result;
then it worked. I need only MySummary object but I should write more classes to make it work.
After reading your solution I came up with one that doesn't need an extra class:
private static async Task<U> Execute<U>(HttpClient client, string path)
{
U output = default(U);
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
var jsonAsString = await response.Content.ReadAsStringAsync();
output = JsonConvert.DeserializeObject<U>(jsonAsString);
}
else
{
throw new ApplicationException(string.Format("Response message is not OK. Issues in action: {0}", path));
}
return output;
}
For the sake of future readers, I think the correct approach is using ReadAsAsync overload that takes IEnumerable<MediaTypeFormatter> and provide a formatter with the same settings used on the server for serialization. That should fix it.
It is possible to use at client ReadAsAsync with MyListResponse directly (in consequence without ResponseWrapper). To do this, you can define "BodyStyle = WebMessageBodyStyle.Bare" in the operation contract of "apiuri" in stead of "BodyStyle = WebMessageBodyStyle.Wrapped" (server side, i.e. service contract).

fluent NHibernate many to many creates additional table

I have two tables "recall" and "service" and I need many to many between them.
I use fluent NHibernate mapping but it creates additional table with name "servicetorecall"
public class Recall : BaseDomain
{
public virtual string Name { get; set; }
public virtual string PersonPosition { get; set; }
public virtual string RecallText { get; set; }
private ICollection<Service> _services = new List<Service>();
public virtual ICollection<Service> Services
{
get { return _services; }
set { _services = value; }
}
}
public class Service : BaseDomain
{
public virtual string Name { get; set; }
public virtual string Url { get; set; }
public virtual string ImgPath { get; set; }
public virtual string ShortContent { get; set; }
public virtual string Content { get; set; }
public virtual bool ServiceIsVisible { get; set; }
ICollection<Recall> _recalls = new List<Recall>();
public virtual ICollection<Recall> Recalls
{
get { return _recalls; }
set { _recalls = value; }
}
}
Mappings :
class RecallMappingOverride : IAutoMappingOverride<Recall>
{
public void Override(AutoMapping<Recall> mapping)
{
mapping.Cache.ReadWrite();
mapping.HasManyToMany(q => q.Services).Table(MappingNames.RECALLS_RELATIONS)
.ParentKeyColumn(MappingNames.RECALL_ID)
.ChildKeyColumn(MappingNames.SERVICE_ID).Inverse().Cascade.All();
}
}
public class ServiceMappingOverride : IAutoMappingOverride<Service>
{
public void Override(AutoMapping<Service> mapping)
{
mapping.Cache.ReadWrite();
mapping.HasManyToMany(q => q.Recalls).Table(MappingNames.RECALLS_RELATIONS) .ParentKeyColumn(MappingNames.SERVICE_ID).ChildKeyColumn(MappingNames.RECALL_ID)
.Inverse().Cascade.All();
}
}
I tried to change cascades but this didn't help. Also I did the same with other entities and it works correctly what type of magic is it?
How do you define "correct", what do you want to achieve?
I never heard of any clean solution for many to many relations which doesn't use pivot table.
[quick glimpse at your mappings]: only one of the "ManyToMany" should be Inverse

ria domain service is setting a client-side property on callback

I'm using RIA domain services, with entity framework 4 and silverlight 4. When I save changes, when the service call returns, some domain service functions are called wich sets a value to "" that should not be changed at all.
I have two entities
service.metadata.cs:
public partial class EntityA
{
[Key]
public Guid EntityA_Id { get; set; }
public string Name { get; set; }
public int EntityB_Id { get; set; }
[Include]
public EntityB entityB { get; set; }
}
public partial class EntityB
{
[Required]
public string Name { get; set; }
public int EntityB_Id { get; set; }
public EntityCollection<EntityA> entityA { get; set; }
}
On the client side I have a Extra property on EntityA to expose the Name property od EntityB. The server side and domain service never need to know about this property, its for GUI only.
public partial class EntityA
{
//Tags I have tried:
//[IgnoreDataMember]
//[XmlIgnore]
//[Ignore]
//[Exclude]
public string NameOf_EntityB
{
get
{
return this.entityB == null ? string.Empty : this.entityB.Name;
}
set
{
this.entityB.Name = value;
}
}
}
If I edit the entityA's name and call serviceContext.SubmitChanges(), when the call returns some domain service process is setting the EntityA.NameOf_EntityB = ""; So from a user point of view, they save one value and the other blanks out.
I need to stop this from happening. I have tried various data attributes, but they either don't work on the client side, or have no effect.
Any idea what to do to stop the domain service from changing this value?
here's the callstack right before the value is changed:
System.ServiceModel.DomainServices.Client!System.ServiceModel.DomainServices.Client.ObjectStateUtility.**ApplyValue**(object o, object value, System.Reflection.PropertyInfo propertyInfo, System.Collections.Generic.IDictionary<string,object> originalState, System.ServiceModel.DomainServices.Client.LoadBehavior loadBehavior) + 0x74 bytes
System.ServiceModel.DomainServices.Client!System.ServiceModel.DomainServices.Client.ObjectStateUtility.ApplyState(object o, System.Collections.Generic.IDictionary<string,object> stateToApply, System.Collections.Generic.IDictionary<string,object> originalState, System.ServiceModel.DomainServices.Client.LoadBehavior loadBehavior) + 0x330 bytes
System.ServiceModel.DomainServices.Client!System.ServiceModel.DomainServices.Client.Entity.ApplyState(System.Collections.Generic.IDictionary<string,object> entityStateToApply, System.ServiceModel.DomainServices.Client.LoadBehavior loadBehavior) + 0x68 bytes
System.ServiceModel.DomainServices.Client!System.ServiceModel.DomainServices.Client.Entity.Merge(System.ServiceModel.DomainServices.Client.Entity otherEntity, System.ServiceModel.DomainServices.Client.LoadBehavior loadBehavior) + 0x5a bytes
System.ServiceModel.DomainServices.Client!System.ServiceModel.DomainServices.Client.DomainContext.ApplyMemberSynchronizations(System.Collections.Generic.IEnumerable<System.ServiceModel.DomainServices.Client.ChangeSetEntry> changeSetResults) + 0x10e bytes
System.ServiceModel.DomainServices.Client!System.ServiceModel.DomainServices.Client.DomainContext.ProcessSubmitResults(System.ServiceModel.DomainServices.Client.EntityChangeSet changeSet, System.Collections.Generic.IEnumerable<System.ServiceModel.DomainServices.Client.ChangeSetEntry> changeSetResults) + 0x262 bytes
System.ServiceModel.DomainServices.Client!System.ServiceModel.DomainServices.Client.DomainContext.CompleteSubmitChanges(System.IAsyncResult asyncResult) + 0x1cb bytes
System.ServiceModel.DomainServices.Client!System.ServiceModel.DomainServices.Client.DomainContext.SubmitChanges.AnonymousMethod__5() + 0x2e bytes
Edit:
found a work around for now. In the callback of the ServiceContext.submitChanges() call I can call ServiceContext.RejectChanges() to undo the change that was made to EntityB. I don't trust this solution though as other changes could have been made before the async call returns and those changes would be rejected as well. The ideal solution would be to have that value ignored and NOT set at all
You may need to tell WCF RIA a little more about your entities with some attributes:
public partial class EntityA
{
[Key]
public Guid EntityA_Id { get; set; }
public string Name { get; set; }
public int EntityB_Id { get; set; }
[Include]
[Association("EntityA-EntityB", "EntityA_Id", "EntityB_Id", IsForeignKey=false)]
public EntityB entityB { get; set; }
}
public partial class EntityB
{
[Required]
public string Name { get; set; }
[Key]
public int EntityB_Id { get; set; }
public Guid EntityA_Id { get; set; }
[Include]
[Association("EntityA-EntityB", "EntityA_Id", "EntityB_Id", IsForeignKey=true)]
public EntityCollection<EntityA> entityA { get; set; }
}
Here's my solution:
private bool isExpanded = false;
public bool IsExpanded
{
get { return isExpanded; }
set
{
string stack = (new System.Diagnostics.StackTrace()).ToString();
if (!stack.Contains("ObjectStateUtility.ApplyState") && isExpanded != value)
{
isExpanded = value;
RaisePropertyChanged("IsExpanded");
}
}
}