I have the following classes.
public class Course
{
public string CourseNumber { get; set; }
public List<PriceGroup> PriceGroups { get; set; }
}
public class PriceGroup
{
public int Id { get; set; }
public string CourseNumber { get; set; }
}
I have a service that fetches data from the database. Courses is an IEnumerable<Course> and priceGroups is a IEnumerable<PriceGroup>. As you can see in the Course class, a course has a List<PriceGroup>. I need to somehow add each PriceGroup in the IEnumerable<PriceGroup> to the list in each course in the IEnumerable<Course> if they match on the variable CourseNumber. So I will end up with a correct Course containing the right List<PriceGroup>'s
public async Task<IEnumerable<Course>> GetAllAsync()
{
//courses is an IEnumerable<Course>
var courses = await _courseRepository.GetAllAsync();
//priceGroups is an IEnumerable<PriceGroup>
var priceGroups = await _coursePriceGroupRepository.GetAllAsync();
courses.GroupJoin(priceGroups, c => c.PriceGroups.Select(i => i.CourseNumber), pg => pg.CourseNumber, (c, pg) => new
{
});
return await courses;
}
To do this I've tried to do a GroupJoin but without success. I have not finished the attempt as I am completely stuck mentally. Perhaps I am trying to do something that is not possible with a GroupJoin. Does anyone know a way to achieve what I need?
So you want all (or some) Courses, each Course with all (or some of) its PriceGroups.
There is a straightforware one-to-many relation betweeen Courses and PriceGroups: Every Course has zero or more PriceGroups, every PriceGroup belongs to exactly one Course, namely the Course that foreign key CourseNumber refers to.
You are right, whenever you want items with their sub-items you use GroupJoin.
public async Task<IEnumerable<Course>> GetAllAsync()
{
IEnumerable<Course> courses = await _courseRepository.GetAllAsync();
IEnumerable<PriceGroup> priceGroups = await _coursePriceGroupRepository.GetAllAsync();
var queryCoursesWithPriceGroups = courses.GroupJoin( // GroupJoin Courses
priceGroups, // with PriceGroups
course => course.CourseNumber, // from every Course take primary key
priceGroup => priceGroup.CourseNumber, // from every PriceGroup take foreign key
(course, priceGroupsOfThisCourse) => new // from every Course with
{ // its priceGroups make one new
// Select the Course properties you plan to use:
Id = course.Id,
Name = course.Name,
StartDate = course.StartDate,
...
PriceGroups = priceGroupsOfThisCourse.Select(priceGroup => new
{
// Select only the PriceGroup properties that you plan to use
Id = priceGroup.Id,
Name = priceGroup.Name,
...
// not needed, you know the value:
// CourseId = priceGroup.CourseId
})
.ToList(),
});
// Note: this is a query. It is not executed yet!
return await queryCoursesWithPriceGroups.ToListAsync();
//Assuming courseNumber is unique in Courses table
return Courses.Select( c=> { c.PriceGroup=priceGroup.Where(p=>p.CourseNumber==c.CourseNumber).ToList(); return c; });
I did a practical example, obviously you should substitute the fake methods with repository ones:
class Program
{
static async void Main(string[] args)
{
var courses = GetAllAsync().Result;
}
private static async Task<IEnumerable<Course>> GetAllAsync()
{
//courses is an IEnumerable<Course>
var courses = await GetCoursesAsync();
//priceGroups is an IEnumerable<PriceGroup>
var priceGroups = await GetPriceGroups();
foreach (var course in courses)
{
foreach (var priceGroup in priceGroups.Where(x => x.CourseNumber == course.CourseNumber))
{
course.PriceGroups.Add(priceGroup);
}
}
return courses;
}
private static async Task<IEnumerable<Course>> GetCoursesAsync()
{
return await Task.FromResult<IEnumerable<Course>>(
new List<Course>() {
new Course{
CourseNumber = "PIZZA1",
PriceGroups = new List<PriceGroup>()
},
new Course{
CourseNumber = "PIZZA2",
PriceGroups = new List<PriceGroup>()
},
new Course{
CourseNumber = "PIZZA3",
PriceGroups = new List<PriceGroup>()
},
}
);
}
private static async Task<IEnumerable<PriceGroup>> GetPriceGroups()
{
return await Task.FromResult<IEnumerable<PriceGroup>>(
new List<PriceGroup>() {
new PriceGroup{
Id = 1,
CourseNumber = "PIZZA1"
},
new PriceGroup{
Id = 2,
CourseNumber = "PIZZA2"
},
new PriceGroup{
Id = 3,
CourseNumber = "PIZZA3"
}
}
);
}
}
public class Course
{
public string CourseNumber { get; set; }
public List<PriceGroup> PriceGroups { get; set; }
}
public class PriceGroup
{
public int Id { get; set; }
public string CourseNumber { get; set; }
}
You should avoid using IEnumerable when you return data from DB, you can incur in unexpected behaviours. I suggest you this approach:
class Program
{
static void Main(string[] args)
{
var courses = GetAllAsync().Result;
}
private static async Task<List<Course>> GetAllAsync()
{
var courses = await GetCoursesAsync();
var priceGroups = await GetPriceGroups();
courses.ForEach(x => { x.PriceGroups.AddRange(priceGroups.Where(y => y.CourseNumber == x.CourseNumber)); });
return courses;
}
private static async Task<List<Course>> GetCoursesAsync()
{
return await Task.FromResult(
new List<Course>() {
new Course{
CourseNumber = "PIZZA1",
PriceGroups = new List<PriceGroup>()
},
new Course{
CourseNumber = "PIZZA2",
PriceGroups = new List<PriceGroup>()
},
new Course{
CourseNumber = "PIZZA3",
PriceGroups = new List<PriceGroup>()
},
}
);
}
private static async Task<List<PriceGroup>> GetPriceGroups()
{
return await Task.FromResult(
new List<PriceGroup>() {
new PriceGroup{
Id = 1,
CourseNumber = "PIZZA1"
},
new PriceGroup{
Id = 2,
CourseNumber = "PIZZA2"
},
new PriceGroup{
Id = 3,
CourseNumber = "PIZZA3"
}
}
);
}
}
public class Course
{
public string CourseNumber { get; set; }
public List<PriceGroup> PriceGroups { get; set; }
}
public class PriceGroup
{
public int Id { get; set; }
public string CourseNumber { get; set; }
}
I made a version using GroupJoin too:
class Program
{
static void Main(string[] args)
{
var courses = GetAllAsync().Result;
}
private static async Task<List<Course>> GetAllAsync()
{
var courses = await GetCoursesAsync();
var priceGroups = await GetPriceGroups();
var groupedData = courses.GroupJoin(priceGroups,
course => course.CourseNumber,
priceGroup => priceGroup.CourseNumber,
(course, priceGroupsCollection) =>
new
{
CourseNumber = course.CourseNumber,
PriceGroups = priceGroupsCollection.ToList()
});
courses.ForEach(x => { x.PriceGroups = groupedData.FirstOrDefault(y => y.CourseNumber == x.CourseNumber)?.PriceGroups ?? new List<PriceGroup>(); });
return courses;
}
private static async Task<List<Course>> GetCoursesAsync()
{
return await Task.FromResult(
new List<Course>() {
new Course{
CourseNumber = "PIZZA1",
PriceGroups = new List<PriceGroup>()
},
new Course{
CourseNumber = "PIZZA2",
PriceGroups = new List<PriceGroup>()
},
new Course{
CourseNumber = "PIZZA3",
PriceGroups = new List<PriceGroup>()
},
}
);
}
private static async Task<List<PriceGroup>> GetPriceGroups()
{
return await Task.FromResult(
new List<PriceGroup>() {
new PriceGroup{
Id = 1,
CourseNumber = "PIZZA1"
},
new PriceGroup{
Id = 2,
CourseNumber = "PIZZA2"
},
new PriceGroup{
Id = 3,
CourseNumber = "PIZZA3"
},
new PriceGroup{
Id = 4,
CourseNumber = "PIZZA1"
}
}
);
}
}
public class Course
{
public string CourseNumber { get; set; }
public List<PriceGroup> PriceGroups { get; set; }
}
public class PriceGroup
{
public int Id { get; set; }
public string CourseNumber { get; set; }
}
I would like to use unique natural key instead of build in Guid CorrelationId in Masstransit Sagas. However it seems not really work.
If I send twice Initial event with same key value, two Sagas created in repository - expected is single instance.
Same scenario with using CorrelationId produces one Saga instance in repository.
Any ideas, why second Saga instance created with same key, ignoring correlation statement?
Here is short example with unit test:
[TestFixture]
public class SagaTests
{
[Test]
public void TestSagaInitialization()
{
var testSaga = new TestSaga();
var testSagaRepository = new InMemorySagaRepository<TestSagaState>();
var busControl = Bus.Factory.CreateUsingInMemory(
cfg =>
{
cfg.ReceiveEndpoint("test_queue",
e =>
{
e.StateMachineSaga(testSaga, testSagaRepository);
});
});
busControl.Start();
busControl.Publish(new TestSagaInitEvent() { UniqueNaturalKey = 1 }).Wait();
busControl.Publish(new TestSagaInitEvent() { UniqueNaturalKey = 2 }).Wait();
// Message with same natural key published again
busControl.Publish(new TestSagaInitEvent() { UniqueNaturalKey = 1 }).Wait();
// Wait till all messges consumed
var till = DateTime.Now.AddSeconds(1);
while (DateTime.Now < till) { System.Threading.Thread.Sleep(50); }
busControl.Stop();
var sagaInstances = testSagaRepository.Where(x => x.UniqueNaturalKey == 1).Result.ToList();
Assert.AreEqual(1, sagaInstances.Count, "Dublicate initial events with same Natural Key value must not create new Saga instance");
}
}
public class TestSagaState : Automatonymous.SagaStateMachineInstance
{
public Guid CorrelationId { get; set; }
public int CurrentState { get; set; }
public long UniqueNaturalKey { get; set; }
}
public class TestSaga : MassTransitStateMachine<TestSagaState>
{
public TestSaga()
{
InstanceState(x => x.CurrentState);
Event(() => SagaInitiated, x => x.CorrelateById(state => state.UniqueNaturalKey, context => context.Message.UniqueNaturalKey)
.SelectId(context => Guid.NewGuid()));
Initially(
When(SagaInitiated)
.Then(context =>
{
context.Instance.UniqueNaturalKey = context.Data.UniqueNaturalKey;
})
.TransitionTo(Initiated)
);
}
public State Initiated { get; private set; }
public Event<TestSagaInitEvent> SagaInitiated { get; private set; }
}
public class TestSagaInitEvent
{
public long UniqueNaturalKey { get; set; }
}
I have a Web API that returns a list of objects, when the client passes Accept application/json I want my globally registered json formatter to include TypeNameHandling for the derived types during serialization. However this doesn't work and I can't see why this shouldn't work ?
My objects
public class BaseClass
{
public int Id { get; set; }
}
public class SubClass : BaseClass
{
public string SubClassProp { get; set; }
}
public class SubClassA : SubClass
{
public string SubClassAProp { get; set; }
}
public class SubClassB : SubClass
{
public string SubClassBProp { get; set; }
}
WebApiConfig
public static void Register(HttpConfiguration config)
{
var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
var settings = jsonFormatter.SerializerSettings;
settings.Formatting = Formatting.Indented;
settings.NullValueHandling = NullValueHandling.Ignore;
settings.TypeNameHandling = TypeNameHandling.Auto;
}
Web API Controller
public class MyController : ApiController
{
[HttpGet]
public async Task<IList<BaseClass>> GetClasses()
{
return new List<BaseClass>
{
new SubClassA
{
Id = 1,
SubClassProp = "SubClass",
SubClassAProp = "SubClassAProp"
},
new SubClassB
{
Id = 2,
SubClassProp = "SubClass",
SubClassBProp = "SubClassBProp"
}
};
}
}
Call from API Client in same solution
var client = new HttpClient() { BaseAddress = new Uri("uri goes here...")}
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var resp = await client.GetAsync("uri goes here..."));
var jsonContent = await resp.Content.ReadAsStringAsync();
var ListOfClasses = JsonConvert.DeserializeObject<IList<BaseClass>>(jsonContent, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
I'am expecting to get one element which is SubClassA and one that is SubClassB, but both is BaseClass ?
I also want it to be possible to Deserialize json to object in Post method.
And this should be possible for both json and xml
Morning,
I'm testing model validation in NancyFX (1.4.1). The validation works fine when hitting the API endpoint, but fails in the tests. I'm using the default bootstrapper. The tests are in a separate project. Both projects have nancy.validation.fluentvalidation package referenced. Is there some more configuration required in the Test browser?
Thanks in advance!
Model:
public class CreateServiceCommand
{
[Required(AllowEmptyStrings = false)]
public string TestField { get; set; }
}
Module under test:
public class ServiceModule : NancyModule
{
private readonly IServiceCreateRequestedListener _listener;
public ServiceModule(IServiceCreateRequestedListener listener)
{
_listener = listener;
Post["/services/create"] = parameters =>
{
var request = this.Bind<CreateServiceCommand>();
var result = this.Validate(request);
if(!result.IsValid) return HttpStatusCode.BadRequest;
_listener.CreateServiceRequested(request);
return "";
};
}
}
Test snippet:
[SetUp]
public void Setup()
{
var browser = new Browser(with =>
{
with.Module<ServiceModule>();
with.Dependency<IServiceCreateRequestedListener>(this);
});
_result = browser.Post("/services/create", with =>
{
with.HttpRequest();
});
}
[Test]
public void ShouldReturnBadRequest
{
Assert.That(_result.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
The data annotation on the CreateServiceCommand as shown in the demo code didn't work.
I had to implement an instance of
AbstractValidator<CreateServiceCommand>
from FluentValidation
ActionBase, ActionA, ActionB and ActionC are Entities (from a database). ActionA, ActionB and ActionC are derived type of ActionBase.
ActionB and ActionC implements ISpecialAction with a SpecialProperty.
ex :
public interface ISpecialAction
{
Guid SpecialProperty { get; }
}
public partial class ActionBase
{
public objectX OnePropertyBase { get; set; }
}
public partial class ActionA : ActionBase
{
public objectY OnePropertyA { get; set; }
}
public partial class ActionB:ActionBase,ISpecialAction
{
public objectZ OnePropertyB { get; set; }
public Guid SpecialProperty
{
get
{
return OnePropertyB.ID;
}
}
}
public partial class ActionC : ActionBase ,ISpecialAction
{
public objectW OnePropertyC { get; set; }
public Guid SpecialProperty
{
get
{
return OnePropertyC.ID;
}
}
}
My problem is that SpecialProperty is build from other Properties of the objects (ActionB or ActionC) and when the cast (to ISpecialAction) is done, OtherProperty and OtherProperty2 are null.
I tried :
GetActionBase().ToList().Where(x=>x is ISpecialAction && ((dynamic) x).SpecialProperty== p_SpecialProperty);
GetActionBase().ToList().Where(x=>x is ISpecialAction && ((ISpecialAction) x).SpecialProperty== p_SpecialProperty);
GetActionBase().ToList().OfType<ISpecialAction>().Where(x => x.SpecialProperty== p_SpecialProperty).Cast<ActionBase>();
return GetActionOnGoing().ToList().OfType<ICityAction>().Cast<ActionBase>().Where(x => ((dynamic)x).CityId == p_CityId);
remark : OfType<> doesn't works with an Interface in Linq to entities but is ok in Linq to object
How do I access my property interface without knowing the type of the object?
I might missed something but this is Ok with the code you provided :
public class objectX
{
}
public class objectY
{
}
public class objectZ
{
public Guid ID { get { return Guid.NewGuid();} }
}
public class objectW
{
public Guid ID { get { return new Guid(); } }
}
class Program
{
private static Guid p_SpecialProperty;
static void Main(string[] args)
{
var result = GetActionBase().ToList().Where(x => x is ISpecialAction && ((dynamic)x).SpecialProperty == p_SpecialProperty).FirstOrDefault();
var result1 = GetActionBase().ToList().Where(x => x is ISpecialAction && ((ISpecialAction)x).SpecialProperty == p_SpecialProperty).FirstOrDefault();
var result2 = GetActionBase().ToList().OfType<ISpecialAction>().Where(x => x.SpecialProperty == p_SpecialProperty).Cast<ActionBase>().FirstOrDefault();
}
private static IEnumerable<ActionBase> GetActionBase()
{
return new List<ActionBase> {new ActionA{OnePropertyA= new objectY()}, new ActionB{OnePropertyB=new objectZ()},new ActionC{OnePropertyC=new objectW()} };
}
}
Not sure if I exactly understand your question, but could you try using an intermediate interface, such as:
public interface ISpecialActionB : ISpecialAction
{
objectZ OnePropertyB { get; set; }
}
public class ActionB : ActionBase, ISpecialActionB
{
//same stuff
}
and casting to that instead.
var b = new ActionB{OnePropertyB = new Whatever()};
var bAsSpecial = b as ISpecialActionB;
var whatever = b.OnePropertyB; // should not be null
It' ok.
Your example run very well without problem so I searched in a other way : AutoMapper.
l_List.Actions = Mapper.Map<List<ActionBase>, Action[]>(l_ActionManagement.GetActionBySpecialId(l_Special.ID).ToList());
The problem was not interfaces or Linq queries but it was that automapper need an empty constructor and in this constructor, I need to initialize OnePropertyB and OnePropertyC to compute SpecialProperty.
Thanks