I have a Fluent NHibernate project I'm working on, and doing some testing I have run into a very strange error:
The entity '<>c__DisplayClass3' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id).
The related entity reported is:
{Name = "<>c__DisplayClass3" FullName = "TPLLCPortal.Domain.Account+<>c__DisplayClass3"}
I don't have any class named DisplayClass, but I do have an Account entity. I'm using a primary key convention that looks like this:
public class PrimaryKeyConvention : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
instance.GeneratedBy.GuidComb();
}
}
My Account class inherits from an EntityBase class that declares the ID as:
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public virtual Guid Id { get; protected internal set; }
I'm confident that I'm setting up the configuration properly and that the conventions are being picked up, but just in case I added an override and specifically mapped the ID for the Account class. No dice.
Any ideas what's going on here?
I'm using FNH 1.3.0.733 with NHibernate 3.3.1.4000 (both loaded off NuGet).
Looks like I figured it out. This SO answer had the key. Because some of the methods on the class use lambdas, the compiler creates classes that you can exclude in the DefaultAutomappingConfiguration by specifying !type.IsDefined(typeof(CompilerGeneratedAttribute), false) as part of the ShouldMap override.
Related
We have starting new project in our company. We finalize the architecture as follows
There are 5 different project as follows
1) BusinessEntities(Class Library) which contains DataContract as follows
[DataContract]
public class Cities
{
/// <summary>
/// Gets or sets City Id
/// </summary>
[DataMember]
public int Id { get; set; }
/// <summary>
/// Gets or sets City name
/// </summary>
[DataMember]
[Display(Name = "CityName", ResourceType = typeof(DisplayMessage))]
[Required(ErrorMessageResourceName = "CityName", ErrorMessageResourceType = typeof(ErrorMessage))]
[RegularExpression(#"[a-zA-Z ]*", ErrorMessageResourceName = "CityNameAlphabates", ErrorMessageResourceType = typeof(ErrorMessage))]
[StringLength(50, ErrorMessageResourceName = "CityNameLength", ErrorMessageResourceType = typeof(ErrorMessage))]
public string Name { get; set; }
}
2) Interface which contains
[ServiceContract]
public interface ICity : IService<CityViewModel>
{
[OperationContract]
Status Add(Cities entity);
}
3) DAL which contains implementation of WCF service
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class City : ICity
{
public Status Add(Cities entity)
{
//Insert Logic goes here
}
}
4) Webcomponent which call the WCF service
public class City
{
public static Status Add(Cities entity)
{
using (var service = new WcfServiceProvider<ICity>())
{
return service.GetProxy().Add(entity);
}
}
}
5) UI (Asp.Net MVC Project) which call webcomponent to access service
City.Add(entity);
Now we finalize this structure. But the problem is how to use Repository Pattern for Unit Testing? Is it possible to use repository pattern on this structure if yes how? Or is there any other pattern we have to use?
I recommend that you read about seperation of concerns. Right now you are using your business object as DTO and BO. That effectivly couples your WCF service with your domain layer AND with the UI layer.
That means that version control will be impossible. If you want to do any change in the UI or in the DL you have to make sure that all changes are made in both layers as the UI won't be able to talk with the BL otherwise.
It's much better to have dedicated DTOs since you then can handle versioning issues a lot easier (like default values or a newly introduced property etc).
Your naming does not make sense. Your Cities class contains ONE city, right? Why don't you name it City.
[ServiceContract]
public interface ICity : IService<CityViewModel>
{
[OperationContract]
Status Add(Cities entity);
}
Can you explain what the service definition is? I don't see the relation between the view model and the DTO. Same thing goes here. The name ICity is misleading. If it's a repository name it as such. Most of us use the City name to point out the object that we work with and use other names like ICityService or ICityRepository to point out the access technologies.
Now to the real question:
But the problem is how to use Repository Pattern for Unit Testing?
You don't.
The only responsibilty of repositories is to load and store data in the data source. You can of course mock the DbConnection etc. But that doesn't guarantee anything at all since the repositories is effectivly coupled to the data source. If you use mocks you'll still get failures from incorrect SQL queries, invalid column types, incorrect table relations etc.
Hence if you truly want to make sure that the repositories work you have to query a database.
I have an abstract base class which inherits Sharp Arch's Entity class:
/// <summary>
/// defines an entity that will ne indexed by a search crawler and offered up as full-text searchable
/// </summary>
public abstract class IndexedEntity : Entity
{
[DocumentId]
public override int Id
{
get { return base.Id; }
protected set { base.Id = value; }
}
}
This is to a legacy db and actually the Id column is called "HelpPageID", so I have some mapping override as:
mapping.Id(x => x.Id, "HelpPageID");
The generated sql for querying HelpPage works fine when I simply inherit Entity. But inheriting IndexedEntity, when translated to sql, the column name override is ignored and instead Id is used for the column, thus failing.
Edit
Seems a general issue with an override as placing the override directly in the class has the same net effect
mapping overrides are only executed for the exact type not types which subclass the type in the mappingoverride. you have to specify an override for the subclass.
I must be doing something really wrong as this seems like a very simple extension that causes an error when you try to compile the code.
So...we have a Customer Table and in that table we have a Customer_ID. We only store the Customer ID and the rest of the data comes from a Customer Truth Center.
This Customer table is referenced and creates a Entity Customer object when we generate our entity EDMX file.
We take this Customer ID and fetch the rest of the Customer info from a WCF service thats our Customer truth center. This returns the Name, Age and such.
So...we want to extend the existing Customer entity with these additional properties however we done "persist" these in our Customer database.
Hence we created a Partial Class to extend our Entity Customer like this:
namespace UsingRIAServices.Web.Models
{
public partial class Customer
{
public string Name { get; set;}
public int Age { get; set;}
}
}
This didnt work and when you build you get the following error.
Entity "UsingRIAServices.Web.Models.Customer' has a property 'CustomerReference' with an supported type.
So...if you go into the Customer.Designer.cs you see this propery
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[BrowsableAttribute(false)]
[DataMemberAttribute()]
public EntityReference<Customer> CustomerReference
{
blah, blah
}
And note the type Customer in the EntityReference which is now an extended class with our partial.
So...I added [Datamember] to each item in our partial class...same error. I tried to [Exclude] it and get the same error.
Why is something that seems so simple and direct so difficult. Please help us figure out how to extend an entity partial class. Can you do this with data that is not in the table?
Thanks
The trick is to add your class to the Models\Shared folder of your web project and name your class Customer.shared.cs.
You then get rid of all of the using statements from your new class and add the "partial" keyword to the new class. For example:
namespace XXXX.Web
{
public partial class Customer
{
public string FullName
{
get
{
return FirstName + " " + LastName;
}
}
}
}
I have a convention for my ids, which automatically maps properties with a name of Id as the identifier. As requirements are being fleshed out I need to tweak a domain model so naturally I went online and found that I need to create a class that inherits from IAutoMappingOverride<T>.
My convention:
public class PrimaryKeyConvention : IIdConvention, IIdConventionAcceptance
{
public void Apply(IIdentityInstance instance)
{
instance.Column("Id");
instance.GeneratedBy.SeqHiLo(instance.Name, "10");
}
public void Accept(IAcceptanceCriteria<IIdentityInspector> criteria)
{
criteria.Expect(x => x.Generator, Is.Not.Set);
}
}
My override:
public class LocateMappingOverride : IAutoMappingOverride<Locate>
{
public void Override(AutoMapping<Locate> mapping)
{
mapping.Map(x => x.SendTo).Not.Nullable();
}
}
The convention does work as expected if I remove my override.
The exception I get is The entity 'LocateMappingOverride' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id)..
Is it possible to use conventions in conjunction with mapping overrides?
The answer is - yes, automapping can work with overrides.
Look what the error said. The problem is not with Locate entity, but with LocateMappingOverride entity, and that class should not be treated as entity, of course. You must have IAutomappingConfiguration configured so that FluentNHibernate's rule what to treat as entity includes LocateMappingOverride, too. And it does not have an Id mapped, indeed.
You should either:
change your IAutomappingConfiguration so that classes that implements IAutoMappingOverride<> are excluded
move the override outside the scope that is searched for entities
or introduce a common marker interface that all entities need to implement, i.e. IEntity and change IAutomappingConfiguration rules respectively.
Take this class as example:
public class Category : PersistentObject<int>
{
public virtual string Title { get; set; }
public virtual string Alias { get; set; }
public virtual Category ParentCategory { get; set; }
public virtual ISet<Category> ChildCategories { get; set; }
public /*virtual*/ void Add(Category child)
{
if (child != null)
{
child.ParentCategory = this;
ChildCategories.Add(child);
}
}
}
When running the application without the virtual keyword of add method, I getting this error:
method Add should be 'public/protected virtual' or 'protected internal virtual'
I understand why properties need to declare as virtual, because thay need to be overridden by the lazy loading feature.
But I don't understand why Methods need to be declare as virtual... they need to be overridden for what reason?
This very confusing!
Methods as well need to be virtual because they could access fields. Consider this situation:
class Entity
{
//...
private int a;
public virtual int A
{
get { return a; }
}
public virtual void Foo()
{
// lazy loading is implemented here by the proxy
// to make the value of a available
if (a > 7)
// ...
}
}
I believe this is required for the lazy-loading feature in NHibernate where NHibernate creates proxies of your entity and controls all access to it. This is why every single method and property must be virtual. Basically, if there is a member doing anything with the entity, NH need to know about it and tap into it.
Like mentioned earlier, in order for NHibernate to do the 'magic' it creates proxy classes which inherit from your entities (Category in your case). However, if you make your entities implement an interface, it will use that interface to create a proxy instead of concrete types. This way, you wouldn't have to mark everything virtual.
EDIT: Some corrections... According to this, i am compelled to say that it almost looks like NH doesn't really do anything with virtual methods, after all. And i even read someone saying that they removed this run-time check from the NH core assembly just to get around it. My assumption would be that it is an older requirements which hasn't been removed. The cool thing is that it looks like there is an initiative to use PostSharp for static proxies, so your classes won't have to declare anything virtual for NH to generate proxies. The bad thing is that it looks like it's been stuck in a branch for almost two years.