I'm starting to work with Masstransit sagas and I built an example, if I use InMemorySagaRepository everything works fine, but when use Redis repository the Increase message is moved to the Consumer_Error queue in RabbitMq with the error message: 'Value cannot be null. Parameter name: key' and this stack trace:
at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
at Automatonymous.AutomatonymousStateMachine`1.GetState(String name)
at Automatonymous.Accessors.RawStateAccessor`1.Automatonymous.StateAccessor<TInstance>.Get(InstanceContext`1 context)
at Automatonymous.Accessors.InitialIfNullStateAccessor`1.<Automatonymous-StateAccessor<TInstance>-Get>d__3.MoveNext()
at Automatonymous.Pipeline.StateMachineSagaMessageFilter`2.<Send>d__5.MoveNext()
at GreenPipes.Filters.ConcurrencyLimitFilter`1.<Send>d__5.MoveNext()
at MassTransit.RedisIntegration.RedisSagaRepository`1.<SendToInstance>d__7`1.MoveNext()
Here's my code:
public class SagaConsumer : MassTransitStateMachine<Number>
{
public SagaConsumer()
{
State(() => Active);
InstanceState(x => x.Status);
Event(() => Created);
Event(() => Increased);
Initially(
When(Created)
.Then(context =>
{
context.Instance.Name = context.Data.Name;
context.Instance.Value = 0;
})
.ThenAsync(context => Console.Out.WriteLineAsync($"New name:{context.Data.Name}"))
.TransitionTo(Active)
);
During(Active,
When(Increased)
.Then(context =>
{
context.Instance.Value = context.Data.Increase ? context.Instance.Value + 1 : context.Instance.Value - 1;
})
.ThenAsync(context => Console.Out.WriteLineAsync($"{context.Instance.Name} increased to value {context.Instance.Value}"))
);
}
public State Active
{
get; set;
}
public Event<ICreateMessage> Created { get; set; }
public Event<IIncreaseMessage> Increased { get; set; }
}
And the configuration code:
var bus = Bus.Factory.CreateUsingRabbitMq(configurator =>
{
var host = configurator.Host(new Uri("rabbitmq://localhost"), h =>
{
h.Username("guest");
h.Password("guest");
});
var redisManager = new PooledRedisClientManager("localhost");
configurator.ReceiveEndpoint("Consumer", endpoint =>
{
endpoint.StateMachineSaga(new SagaConsumer(),
//new InMemorySagaRepository<Number>(),
new RedisSagaRepository<Number>(redisManager),
c => c.UseConcurrencyLimit(10));
});
});
And Saga class:
public class Number : SagaStateMachineInstance, IHasGuidId, IVersionedSaga, CorrelatedBy<Guid>
{
public Guid CorrelationId
{
get;
set;
}
public string Name { get; set; }
public State Status { get; set; }
public int Value { get; set; }
public int Version { get; set; }
public Guid Id
=> CorrelationId;
}
It seems to be related to the Status property of Number class that is null at some point but I'm not sure why or why it's happening only when using Redis
So you should store the status as either an int or a string - and not the State as you have it now. That will resolve the issue you're seeing.
Related
Below is my Hierarchical model
public class MenuModel
{
public int Id { get; set; }
public string Name { get; set; } = null!;
public string? Url { get; set; } = null!;
public string? Icon { get; set; }
public int? ParentId { get; set; }
public MenuModel Parent { get; set; } = null!;
public ICollection<MenuModel>? Children { get; set; } = new List<MenuModel>();
}
Query:
return await _context.Menus//.Include(o => o.Parent)
.Include(m => m.Childrens)
.ThenInclude(m => m.Childrens)
.Where(m => m.ParentId == null)
.ToListAsync();
Query is working fine: How to exclude menu without child elements
Please check below..
I have Added in configuration
services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
})
.AddJsonOptions(options => {
options.JsonSerializerOptions.IgnoreNullValues = true;
});
But in json file it coming.. Is it possible to exclude in ef core itself? I am using latest EF core preview.
EDIT:
Ef itself count with 0 showing ..How avoid with count=0?
You're assigning a default value to the Children property, which is why you always see it in your response. To fix this, simply change the = new List<MenuModel>(); to = null!;
public class MenuModel
{
...
public ICollection<MenuModel>? Children { get; set; } = null!;
}
Currently experiencing issues with Session.Save() not inserting records and producing the following exception:
null id in NHModels.Domain.Activity entry (don't flush the Session after an exception occurs)
at NHibernate.Event.Default.DefaultFlushEntityEventListener.CheckId(Object obj, IEntityPersister persister, Object id, EntityMode entityMode)
at NHibernate.Event.Default.DefaultFlushEntityEventListener.GetValues(Object entity, EntityEntry entry, EntityMode entityMode, Boolean mightBeDirty, ISessionImplementor session)
at NHibernate.Event.Default.DefaultFlushEntityEventListener.OnFlushEntity(FlushEntityEvent event)
at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEntities(FlushEvent event)
at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event)
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
at NHibernate.Impl.SessionImpl.Flush()
at NHibernate.Transaction.AdoTransaction.Commit()
at NHUnitOfWork.Dispose() in NHUnitOfWork.cs:line
at DatabaseActivityOperations.<WriteActivity>d__6.MoveNext() in DatabaseActivityOperations.cs:line 240
My class and mapping look like this.
Activity (For simplicity sake, I've removed several ILists related to this class)
public class Activity {
public Activity() {
Activityschema = new List<ActivitySchema>();
}
public virtual int ActivityKey { get; set; }
public virtual string Activityname { get; set; }
public virtual string Activitydescription { get; set; }
public virtual DateTime Averageactivitytime { get; set; }
public virtual int Averagenumberpeople { get; set; }
public virtual string Worktype { get; set; }
public virtual bool? Canautocomplete { get; set; }
public virtual IList<ActivitySchema> Activityschema { get; set; }
}
ActivityMap
public class ActivityMap : ClassMapping<Activity> {
public ActivityMap() {
Schema("dbo");
Lazy(true);
Id(x => x.ActivityKey, map => { map.Generator(Generators.Identity); });
Property(x => x.Activityname, map => { map.NotNullable(true); map.Length(50); });
Property(x => x.Activitydescription, map => { map.NotNullable(true); map.Length(100); });
Property(x => x.Averageactivitytime, map =>
{
map.NotNullable(true);
map.Type(NHibernateUtil.Time);
});
Property(x => x.Averagenumberpeople, map => { map.NotNullable(true); map.Precision(10); });
Property(x => x.Worktype, map => { map.NotNullable(true); map.Length(50); });
Property(x => x.Canautocomplete);
Bag(x => x.Activityschema, colmap => { colmap.Key(x => x.Column("ActivityKey")); colmap.Inverse(true); }, map => { map.OneToMany(); });
}
}
Finally, here's the Unit of Work class I have:
public class NHUnitOfWork : IDisposable
{
public static string ConnectingString { get; private set; } = #"data source=nh;initial catalog=db;MultipleActiveResultSets=True;";
protected static Configuration _config;
protected static NHibernate.ISessionFactory _sessionFactory;
public NHibernate.ISession Session { get; private set; }
protected NHibernate.ITransaction Transaction { get; set; }
private const System.Data.IsolationLevel ISOLATION_LEVEL = System.Data.IsolationLevel.ReadUncommitted;
private bool RollBack { get; set; } = false;
public NHUnitOfWork(string databaseConnectionString)
{
if (_config == null)
{
var cfg = new Configuration();
cfg.DataBaseIntegration(db =>
{
db.Driver<NHibernate.Driver.SqlClientDriver>();
db.ConnectionString = #"data source=nh;initial catalog=db;MultipleActiveResultSets=True;";
//db.ConnectionString = databaseConnectionString;
db.Dialect<MsSql2012Dialect>();
db.BatchSize = 500;
})
.AddAssembly(typeof(Activity).Assembly)
.SessionFactory()
.GenerateStatistics();
var mapper = new ModelMapper();
mapper.AddMappings(typeof(ActivityMap).Assembly.GetTypes());
cfg.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
_config = cfg;
_sessionFactory = _config.BuildSessionFactory();
}
Session = _sessionFactory.OpenSession();
Transaction = Session.BeginTransaction(ISOLATION_LEVEL);
RollBack = false;
}
public void Commit()
{
Transaction.Commit();
}
public void Rollback()
{
if (Transaction.IsActive) Transaction.Rollback();
}
public void Dispose()
{
if (RollBack)
{
Transaction.Rollback();
}
else
{
Transaction.Commit();
}
Session.Close();
}
}
By this point, I believe my configuration for this is correct. I'm successfully used Session.Query to read data and that doesn't present issues. The problem comes when I write something like this to add a new record:
var activity = new Activity
{
Activityname = "TestActivity",
Activitydescription = "This is a test",
Averagenumberpeople = 1,
Worktype = "Test",
Canautocomplete = false,
Averageactivitytime = new DateTime(1, 1, 1, 0, 55, 55)
};
using (var uow = new NHUnitOfWork(NHUnitOfWork.ConnectingString))
{
uow.Session.Save(activity); // Produces exception here
//This also produces an exception
//uow.Session.Save(activity, Generators.Identity);
}
I think this has something to do with how I'm mapping the ID in ActivityMap and the generator isn't working as expected. I've tried to change it to several other types and get the same exception, or one stating that it's not able to convert to SystemInt32. I've also tried changing the ID to long and specifiying a data type, but no luck. What do I seem to be doing wrong here?
Literally minutes after I posted this, I figured out that the issue was actually with how I was setting the date time.
new DateTime(1, 1, 1, 0, 55, 55)
It didn't like the "1/1/0001" part, so that seemed to be causing the issue, I'm only concerned with the time piece. Changing the year to something like 2001 fixed the insert and it's working fine, the message was just not very descriptive.
After updating to nHibernate (4.0.2.4000 via nuget), the many to many mappings that previously worked now cause mapping exception "Could not determine type for: nHibernateManyToMany.IRole, nHibernateManyToMany, for columns: NHibernate.Mapping.Column(id)"
Seems to be only for many to many, and when the List is a interface (i.e. List<Role> vs List<IRole>).
Example code that now fails:
class Program
{
static void Main(string[] args)
{
var configuration = new Configuration().SetProperty(Environment.ReleaseConnections, "on_close")
.SetProperty(Environment.Dialect, typeof(SQLiteDialect).AssemblyQualifiedName)
.SetProperty(Environment.ConnectionDriver, typeof(SQLite20Driver).AssemblyQualifiedName)
.SetProperty(Environment.CollectionTypeFactoryClass, typeof(DefaultCollectionTypeFactory).AssemblyQualifiedName)
.SetProperty(Environment.CommandTimeout, "0");
var mapper = new ModelMapper();
mapper.AddMappings(new[] { typeof(EmployeeMapping), typeof(RoleMapping) });
var hbmMapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
hbmMapping.autoimport = false;
configuration.AddMapping(hbmMapping);
// this line will fail
var factory = configuration.BuildSessionFactory();
}
}
public class Employee
{
public virtual int Id { get; set; }
public virtual List<IRole> Roles { get; set; }
}
public interface IRole
{
int Id { get; set; }
string Description { get; set; }
}
public class Role : IRole
{
public virtual int Id { get; set; }
public virtual string Description { get; set; }
}
public class EmployeeMapping : ClassMapping<Employee>
{
public EmployeeMapping()
{
Id(c => c.Id, x =>
{
x.Type(NHibernateUtil.Int32);
x.Generator(Generators.Identity);
x.Column("EmployeeId");
});
Bag(x => x.Roles, m =>
{
m.Table("EmployeeRole");
m.Key(km =>
{
km.Column("EmployeeId");
km.NotNullable(true);
km.ForeignKey("FK_Role_Employee");
});
m.Lazy(CollectionLazy.Lazy);
}, er => er.ManyToMany(m =>
{
m.Class(typeof(Role));
m.Column("RoleId");
}));
}
}
public class RoleMapping : ClassMapping<Role>
{
public RoleMapping()
{
Id(c => c.Id, x =>
{
x.Type(NHibernateUtil.Int32);
x.Generator(Generators.Identity);
x.Column("RoleId");
});
Property(x => x.Description, c =>
{
c.Length(50);
c.NotNullable(true);
});
}
}
Any help or suggestions about where we could look for details on how this has changed since v3 would be appreciated.
Turned out to be a bug in nHibernate.
A pull request has been submitted here https://github.com/nhibernate/nhibernate-core/pull/385
I have two classes, each having a domain and a repo version.
DOMAIN:
public class MusicInfo
{
public string Id { get; set; }
public MusicImage Image { get; set; }
public MusicInfo(byte[] image)
{
this.Image = new MusicImage(this, image);
}
}
public class MusicImage
{
public byte[] Blob { get; set; }
public MusicInfo MusicInfo { get; set; }
public string Id { get; set; }
public MusicImage(MusicInfo musicInfo, byte[] blob)
{
if (musicInfo == null)
throw new ArgumentNullException("musicInfo");
if (blob == null)
throw new ArgumentNullException("blob");
this.MusicInfo = musiscInfo;
this.Blob = blob;
}
}
REPO:
public class MusicInfoRepo
{
public virtual long Id { get; set; }
public virtual MusicImageRepo Image { get; set; }
}
public class MusicImageRepo
{
public virtual byte[] Blob { get; set; }
public virtual MusicInfoRepo MusicInfo { get; set; }
public virtual long Id { get; set; }
}
And here are their mappings:
public class MusicInfoRepoMap : HighLowClassMapping<MusicInfoRepo>
{
public MusicInfoRepoMap()
{
Table("MusicInfo");
Id(f => f.Id, m => m.Generator(Generators.HighLow, HighLowMapper));
OneToOne(f => f.Image, m => m.Cascade(Cascade.All));
}
}
public class MusicImageRepoMap : ClassMapping<MusicImageRepo>
{
public MusicImageRepoMap()
{
Table("MusicImage");
Id(f => f.Id, m => m.Generator(Generators.Foreign<MusicImageRepo>(f => f.MusicInfo)));
Property(f => f.Blob, m =>
{
m.NotNullable(true);
m.Column(c => c.SqlType("VARBINARY(MAX)"));
m.Length(Int32.MaxValue);
m.Update(false);
});
OneToOne(f => f.MusicInfo,
m =>
{
m.Cascade(Cascade.None);
m.Constrained(true);
m.Lazy(LazyRelation.NoLazy);
});
}
}
When I am trying to query for a ClassA that has a one to one relationship with ClassB which also has a one to one relationship with MusicInfo, an error occurs that says:
Missing type map configuration or unsupported mapping.
Mapping types:
MusicImageRepo -> Byte[]
Blah.MusicImageRepo -> System.Byte[]
Destination path:
List`1[0]
Source value:
Blah.MusicImageRepo
but here is how i map them:
Mapper.CreateMap<MusicInfo, MusicInfoRepo>();
Mapper.CreateMap<MusicInfoRepo, MusicInfo>();
Mapper.CreateMap<MusicImage, MusicImageRepo>();
Mapper.CreateMap<MusicImageRepo, MusicImage>();
There are no problems when saving these classes.
I really dont get why the error happens.
Will really appreciate your help.
OneToOne says that the other entity/table has the Reference(column). It should not work when both sides have a onetoone mapping since they bounce the responsiblity back and forth.
Also the classes look a bit overcomplicated when all you need is to store the bytes somewhere else.
public class MusicInfoRepo
{
public virtual long Id { get; private set; }
public virtual MusicImageRepo Image { get; private set; }
public MusicInfo(byte[] image)
{
this.Image = new MusicImageRepo(this, image);
}
}
public class MusicImageRepo
{
public virtual MusicInfoRepo MusicInfo { get; private set; }
public virtual byte[] Blob { get; set; }
}
public class MusicInfoRepoMap : HighLowClassMapping<MusicInfoRepo>
{
public MusicInfoRepoMap()
{
Table("MusicInfo");
Id(f => f.Id, m => m.Generator(Generators.HighLow, HighLowMapper));
OneToOne(f => f.Image, m => m.Cascade(Cascade.All));
}
}
public class MusicImageRepoMap : ClassMapping<MusicImageRepo>
{
public MusicImageRepoMap()
{
Table("MusicImage");
ComposedId(m => m.ManyToOne(x => x.MusicInfo));
Property(f => f.Blob, m =>
{
m.NotNullable(true);
m.Column(c => c.SqlType("VARBINARY(MAX)"));
m.Length(Int32.MaxValue);
m.Update(false);
});
}
}
Note: think about it if the seperation between DomainModel and MappedModel really makes sense. NHibernate goes to great length supporting mapping of DomainModels.
I can't figure out how to solve the following problem.
What i need it a relationship from one base class to another, so that every derived class has a relationship with the same table, called 'Item' in my example.
Since this is just an example it doesn't reflect my program. In the real program the relationship with class Item is in a different namespace. Therefore it can't be in the derived class.
The error:
A key is registered for the derived type 'WebApplication1.Client'. Keys must be registered for the root type 'WebApplication1.Base'.
namespace WebApplication1
{
public class Item
{
public int ItemID { get; set; }
}
public class Base
{
public int ID { get; set; }
public int ItemID { get; set; }
public Item Item { get; set; }
}
public class Client : Base
{
public string Name { get; set; }
private List<Project> _projects = null;
public List<Project> Projects
{
get
{
if (_projects == null)
_projects = new List<Project>();
return _projects;
}
}
}
public class Project : Base
{
public string Name { get; set; }
public int ClientId { get; set; }
public Client Client { get; set; }
}
public class Main
{
public static void Test()
{
ContextBuilder<ObjectContext> ContextBuilder = new ContextBuilder<ObjectContext>();
var itemConfig = new EntityConfiguration<Item>();
itemConfig.HasKey(p => p.ItemID);
itemConfig.Property(p => p.ItemID).IsIdentity();
ContextBuilder.Configurations.Add(itemConfig);
var clientConfig = new EntityConfiguration<Client>();
clientConfig.HasKey(p => p.ID);
clientConfig.Property(p => p.ID).IsIdentity();
clientConfig.Property(p => p.Name);
clientConfig.Relationship(p => p.Item).HasConstraint((p, c) => p.ItemID == c.ItemID);
ContextBuilder.Configurations.Add(clientConfig);
var projectConfig = new EntityConfiguration<Project>();
projectConfig.HasKey(p => p.ID);
projectConfig.Property(p => p.ID).IsIdentity();
projectConfig.Property(p => p.Name);
projectConfig.Relationship(p => p.Item).HasConstraint((p, c) => p.ItemID == c.ItemID);
projectConfig.Relationship(p => p.Client).FromProperty(p => p.Projects).HasConstraint((p, c) => p.ClientId == c.ID);
ObjectContext objCtx = ContextBuilder.Create(new SqlConnection(#"Data Source=(local);Initial Catalog=testa;Integrated Security=SSPI;"));
if (!objCtx.DatabaseExists())
objCtx.CreateDatabase();
}
}
}
Look at how do deal with inheritance mapping here: Link
For basic non-relational proberties and how-to reuse them: https://danielwertheim.wordpress.com/2009/11/29/entity-framework-4-how-to-reuse-mappings-and-add-a-concurrency-token/