Is it Possible to created Decimal as a Primery key with Auto Incremented from FNH Mapping??
I ran across the same thing. It appears that fluent-nhibernate is enforcing this constraint.
If you know that the database will allow this (in this case, it should be fine) you can just bypass the constraint like so:
Id(x => x.MyDecimalId).GeneratedBy.Custom<IdentityGenerator>();
This will use the same strategy as Identity() would have, but not raise an exception. In my case, I've added an extension method on IdentityGenerationStrategyBuilder<IdentityPart> as such:
public static class IdentityPartExtensions
{
public static void NumericIdentity(this IdentityGenerationStrategyBuilder<IdentityPart> identityBuilder)
{
identityBuilder.Custom<IdentityGenerator>();
}
}
So then you can just do:
Id(x => x.MyDecimalId).GeneratedBy.NumericIdentity();
To achieve auto increment Identity type must be integral int, long, uint, ulong.
ex :
public class User
{
public virtual int Id { get; set; }
public virtual IList Names { get; set; }
}
Id(x => x.ID).Column("ID").GeneratedBy.Increment();
for decimal properties :
Id(x => x.ID).Column("ID").GeneratedBy.Assigned();
Assign the value while creating a new object. Value should be increment of last generated key in your database.
OR you can just implement a Custom ID Generator for NHibernate.
http://lucisferre.net/2009/07/14/implementing-a-custom-id-generator-for-nhibernate/
Related
I have a table called PODetail with a primary Key of POno and ItemCode and I have the following:
[Route("/podetail/{POno}/{ItemCode}")]
public class UpdatePODetail : IReturn<PODetail> {
public string POno { get; set; }
public string ItemCode { get; set; }
public int ? QtyPend { get; set; }
public decimal ? NewPrice { get; set; }
public bool ? BackOrder { get; set; }
public string ActionCode { get; set; }
public bool ? OpenOrder { get; set; }
}
public class PODetailService : Service {
public object Any(UpdatePODetail request) {
var podetail = Db.SingleFmt<PODetail>("ItemCode = {0} AND POno = {1}", request.ItemCode, request.POno);
// var cap = new CaptureSqlFilter();
try {
Db.Update(podetail);
} catch {
// var sql = string.Join(";\n\n", cap.SqlStatements.ToArray());
}
:
:
try {
Db.Update(podetail);
} catch (Exception ex) {
string error = ex.Message;
}
return podetail;
}
}
I added the Db.Update call at the top just to check to see if there was some issue changing a column, but I get
Violation of PRIMARY KEY constraint 'aaaaaPoDetail_PK'. Cannot insert
duplicate key in object 'dbo.PODetail'.
So then I added the cap = line to see the SQL code which returns
UPDATE "PODetail" SET "NewItemCode"=#NewItemCode, "POno"=#POno, "Vendor"=#Vendor, "ActionCode"=#ActionCode, "Price"=#Price, "NewPrice"=#NewPrice, "CostPrice"=#CostPrice, "QtyOrd"=#QtyOrd, "QtyRcv"=#QtyRcv, "QtySPO"=#QtySPO, "QtyPend"=#QtyPend, "BackOrder"=#BackOrder, "OpenOrder"=#OpenOrder, "OrderDate"=#OrderDate, "InvoiceNo"=#InvoiceNo, "InvoiceVendor"=#InvoiceVendor, "InvoiceDate"=#InvoiceDate, "InvoiceDiscount"=#InvoiceDiscount, "QtyCancel"=#QtyCancel, "Qtylabels"=#Qtylabels, "REOVendor"=#REOVendor, "CurrentRcvQty"=#CurrentRcvQty, "SOPickQty"=#SOPickQty, "SOItem"=#SOItem, "QtyOther"=#QtyOther, "BackOrderCode"=#BackOrderCode WHERE "ItemCode"=#ItemCode
And then it runs fine uncommented -- no exceptions .. if I remove it it gets the Primary Key error
What is the deal -- why do I need that CaptureSqlFilter call -- or what I do I need to change so that it knows both PoNo and ItemCode are primary Keys or the update needs to say WHERE "ItemCode"=#ItemCode AND "POno"=#PONo? It almost seems as if it is trying to do an INSERT vs an UPDATE without the CaptureSqlFilter
Update 1
The documentation said :
Limitations For simplicity, and to be able to have the same POCO class
persisted in db4o, memcached, redis or on the filesystem (i.e.
providers included in ServiceStack), each model must have a single
primary key, by convention OrmLite expects it to be Id although you
use [Alias("DbFieldName")] attribute it map it to a column with a
different name or use the [PrimaryKey] attribute to tell OrmLite to
use a different property for the primary key.
You can still SELECT from these tables, you will just be unable to
make use of APIs that rely on it, e.g. Update or Delete where the
filter is implied (i.e. not specified), all the APIs that end with
ById, etc.
Workaround single Primary Key limitation
A potential workaround to support tables with multiple primary keys is
to create an auto generated Id property that returns a unique value
based on all the primary key fields,
So I tried to add this
public class PODetail {
public string Id { get { return this.ItemCode + "/" + this.POno; } }
public string ItemCode { get; set; }
public string NewItemCode { get; set; }
public string POno { get; set; }
:
}
But when it went to execute :
Db.SingleFmt<PODetail>
It error out with ID not a valid column or column not found or something like that
So I then tried
public class PODetail {
//public string Id { get { return this.ItemCode + "/" + this.POno; } }
[PrimaryKey]
public string ItemCode { get; set; }
public string NewItemCode { get; set; }
[PrimaryKey]
public string POno { get; set; }
:
}
and it worked on the Db.SingleFmt ... and the Db.Update
So then I added back in the CaptureSqlFilter to see what the query looked like and I got
UPDATE "PODetail" SET "NewItemCode"=#NewItemCode, "Vendor"=#Vendor, "ActionCode"=#ActionCode, "Price"=#Price, "NewPrice"=#NewPrice, "CostPrice"=#CostPrice, "QtyOrd"=#QtyOrd, "QtyRcv"=#QtyRcv, "QtySPO"=#QtySPO, "QtyPend"=#QtyPend, "BackOrder"=#BackOrder, "OpenOrder"=#OpenOrder, "OrderDate"=#OrderDate, "InvoiceNo"=#InvoiceNo, "InvoiceVendor"=#InvoiceVendor, "InvoiceDate"=#InvoiceDate, "InvoiceDiscount"=#InvoiceDiscount, "QtyCancel"=#QtyCancel, "Qtylabels"=#Qtylabels, "REOVendor"=#REOVendor, "CurrentRcvQty"=#CurrentRcvQty, "SOPickQty"=#SOPickQty, "SOItem"=#SOItem, "QtyOther"=#QtyOther, "BackOrderCode"=#BackOrderCode WHERE "ItemCode"=#ItemCode AND "POno"=#POno
Which is what I wanted in the first place.
It works but what is the deal can you have the [PrimaryKey] attribute multiple times (it appears so) and also then why didn't the autogenerated Id work? Just wondering if I am missing something or not understanding the documentation correctly.
Oh and sorry for posting in the comments!
what I do I need to change so that it knows both PoNo and ItemCode are
primary Keys
OrmLite's primary limitation is that each Table has a single primary Key.
Also you can use the built-in Profiling or debug logging to view the generated SQL without needing to change code to use CaptureSqlFilter.
I'd also recommend that you don't use the Request DTO for anything other than defining your Service with. You can use the built-in AutoMapping to easily use it to populate your data model.
I have my model classes set up with the integer properties just as they are stored in the database. So a sample model might look like:
public class TaskModel
{
public int TaskId { get; set; }
public int TaskStatus { get; set; }
}
But on my actual business classes I want to use enums, so the matching business class would look like:
public class Task
{
public int TaskId { get; set; }
public Status TaskStatus { get; set; }
}
I then want to use Automapper's LINQ projection features to query these business classes, like:
return db.Tasks.Where( t => t.TaskStatus == 1 ).Project().To<Task>();
But when I do this I get this error:
Unable to create a map expression from System.Int32 to MyNamespace.TaskStatus
I've been able to resolve it by setting up the mapping as such:
Mapper.CreateMap<TaskModel, Task>()
.ForMember(t => t.TaskStatus, opt => opt.MapFrom(m => (TaskStatus)m.TaskStatus))
.ReverseMap();
This seems to work (so far), but my question is there a better or DRYer way to do this. The problem is I will need to do this for a ton of properties across a ton of models and classes. Seems like there should be a simpler way to do what is essentially a simple cast with having to write 100's of lines of mapping code.
You can do this with a type converter:
Mapper.CreateMap<int, TaskStatus>()
.ProjectUsing(src => (TaskStatus)src);
This will be used everywhere. The reason you have to do this is because some LINQ providers have different ways of dealing with enum conversions and persistence, so you have to use the right expression it expects (and AutoMapper doesn't assume it knows what EF or NHibernate or whatever need).
I have a lots of DTOs that must be mapped to my domain's objects. In general, the mapping to monetary values need to apply a rounding rule. That's apply for more than 95% of the cases, but I have some data that need a different rounding rule. I was planned to do the next:
1) Create a ITypeConverter for the general rounding rule, that apply for default in for this type conversions.
2) Create a ValueResolver for the special rounding cases, and specify their use in the member mapping.
I created a test case to try my approach, but after the application of the ValueResolver for the special cases, AutoMapper uses the TypeConverter to get the final value, using the general rule.
That's a wrong approach?? May be I'm missing something??
Then the test case:
[TestFixture]
public class RoundingTests
{
public class MoneyConverter : ITypeConverter<decimal, decimal>
{
public decimal Convert(ResolutionContext context)
{
return Math.Round((decimal)context.SourceValue, 2, MidpointRounding.AwayFromZero);
}
}
public class Money12Resolver : ValueResolver<decimal, decimal>
{
protected override decimal ResolveCore(decimal source)
{
return Math.Round(source, 12, MidpointRounding.AwayFromZero);
}
}
public class PercentResolver : ValueResolver<decimal, decimal>
{
protected override decimal ResolveCore(decimal source)
{
return Math.Round(source, 4, MidpointRounding.AwayFromZero);
}
}
internal class Source
{
public decimal Price { get; set; }
public decimal Percent { get; set; }
public decimal Prorate { get; set; }
}
internal class Destination
{
public decimal Price { get; set; }
public decimal Percent { get; set; }
public decimal Prorate { get; set; }
}
[Test]
public void MappingTest()
{
Mapper.CreateMap<decimal, decimal>().ConvertUsing<MoneyConverter>();
Mapper.CreateMap<Source, Destination>()
.ForMember(d => d.Percent, o => o.ResolveUsing<PercentResolver>().FromMember(s => s.Percent))
.ForMember(d => d.Prorate, o => o.ResolveUsing<Money12Resolver>().FromMember(s => s.Prorate));
Mapper.AssertConfigurationIsValid();
var source = new Source
{
Price = 12345.6789m,
Percent = 0.123456m,
Prorate = 123.123451234512345m
};
var convertion = Mapper.Map<Destination>(source);
Assert.That(convertion, Is.Not.Null);
Assert.That(convertion.Price, Is.EqualTo(12345.68m));
Assert.That(convertion.Percent, Is.EqualTo(0.1235m));
Assert.That(convertion.Prorate, Is.EqualTo(123.123451234512m));
}
}
Test Result:
Expected: 0.1235m
But was: 0.12m
You're telling AutoMapper to convert all decimal->decimal mappings using your MoneyConverter. AutoMapper does, in fact, use your resolver (set a break point to see), but the result of resolving is a decimal value that is then used by the MoneyConverter you applied.
Note: This appears to be the design of AutoMapper; I don't see a way to override a type converter.
Not all of your decimal properties represent money, so you may want to take a different approach. Also ask yourself, are you rounding purely for values used for presentation, or are you potentially losing precision you'd want to retain in those domain objects? If you really need the rounding, you could be explicit and set a resolver per member, skipping the converter completely. Alternatively, you could ignore the members which are exceptions and handle that with a .ConstructUsing(...).
But since your original question relates to using a Resolver and Converter for the same type, here's one way you can make it work:
Basically we want the converter to skip the default conversion for certain properties. We can't do it via configuration, so we'll have to do it at run-time. Assuming you have access to the Destination class, just decorate properties with non-default behavior using a custom attribute.
class PercentAttribute : Attribute
{ }
class Destination
{
[Percent]
public decimal Percent { get; set; }
...
}
In your converter, you can then look for properties which are [Percent] and return the source value, which came from your PercentResolver.
public class MoneyConverter : ITypeConverter<decimal, decimal>
{
public decimal Convert(ResolutionContext context)
{
var propInfo = context.PropertyMap.DestinationProperty.MemberInfo;
bool isPercent = propInfo.GetCustomAttributes(typeof(PercentAttribute), true).Any();
if (isPercent) return (decimal)context.SourceValue;
return Math.Round((decimal)context.SourceValue, 2, MidpointRounding.AwayFromZero);
}
}
If you're going to go that route, just have the converter do Money, Percent, and Money12 conversions based on attributes, and skip the resolvers completely.
What I'm trying to achieve here is to save the current user instance in my ApiConfigurationRecord table. I already dig around the internet, and most of the example is using UserPartRecord. But the troble I encounter is to get the UserPartRecord object itself.
This is my Entity class look like:
public class ApiConfigurationRecord
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual UserPartRecord RegisterBy { get; set; }
}
This is my Migration.cs code look like:
public int Create()
{
SchemaBuilder.CreateTable("ApiConfigurationRecord", table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<int>("RegisterBy_id")
.Column<string>("Name", column => column.NotNull())
);
return 1;
}
This is my Action Controller codes:
public ActionResult Test()
{
var userId = this._orchardServices.WorkContext.CurrentUser.Id;
// below code got error: The non-generic method IContentManager.Query() cannot be used with type arguments
this._orchardServices.ContentManager.Query<UserPart, UserPartRecord>().Where(u => u.Id == userId);
return null;
}
For hours I stuck in this problem. Need to know how to save this User relationship object, and most importantly, get the object itself. Please guide me.
Or you could just do
_orchardServices.WorkContext.CurrentUser.As<UserPart>().Record;
Though you will probably want to check user is not null there too. And as Bertrand Le Roy says, you will also need
using Orchard.ContentManagement;
to make use of the .As extension method.
My super-powers tell me that you are missing the following on top of your controller file:
using Orchard.ContentManagement;
The generic version of the Query method is an extension method that is in this namespace.
I have the following class:
public class FinanceiroLancamento
{
/// <summary>Identificação</summary>
public override int Id { get; set; }
/// <summary>Financeiro caixa</summary>
public FinanceiroLancamentoCaixa FinanceiroLancamentoCaixa { get; set; }
}
public class FinanceiroLancamentoCaixa
{
/// <summary>Identificação</summary>
public override int Id { get; set; }
/// <summary>Identificação do lançamento financeiro</summary>
public int IdFinanceiroLancamento { get; set; }
}
When I try to map and execute migration it´s return:
Property name 'IdFinanceiroLancamento' was already defined.
To solve this problem I needed to comment idfinanceirolancamento and map like this:
HasRequired(e => e.FinanceiroLancamentoCaixa)
.WithRequiredPrincipal()
.Map(m => m.MapKey("IdFinanceiroLancamento"));
The question is:
How can I this FK (FinanceiroLancamento -> FinanceiroLancamentoCaixa) keeping the "IdFinanceiroLancamento { get; set; }"?
This is very important in my case to use later in the class.
Ps: FinanceiroLancamento does not need a FinanceiroLancamentoCaixa, but when FinanceiroLancamentoCaixa exists he needs a FinanceiroLancamento.
Best regards.
Wilton Ruffato Wonrath
Entity Framework requires that 1:1 mappings share the same primary key. In your case, you are trying to use a different member as the mapping id. Also, do not override the base class id, just inherit it.
What you want Is this:
.HasRequired(e => e.FinanceiroLancamentoCaixa)
.WithRequiredPrincipal();
Entity Framework does not allow you to use a 1:1 that is not a shared primary key, so you can't do it in EF. If you absolutely need this, you may have to do it as a stored procedure and call it from EF.
The reason you can't have a 1:1 like this is because the data model allows you to set IdFinanceiroLancamento to the same ID in more than one record, thus breaking your 1:1.
Basically, EF will not allow you to create models with mappings that allow for a violation of the mapping, even if you never create duplicates, it's still a possibility. EF doesn't know about unique constraints either so placing a unique constraint won't tell EF that it's ok.
If you'd like to see this feature, I suggest you vote for it at the EF uservoice:
http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/1050579-unique-constraint-i-e-candidate-key-support